diff --git a/demo/app/app.go b/demo/app/app.go index 0d82bea3..21a2f176 100644 --- a/demo/app/app.go +++ b/demo/app/app.go @@ -428,7 +428,7 @@ func NewMeshApp( keys[meshsecprovtypes.StoreKey], authtypes.NewModuleAddress(govtypes.ModuleName).String(), app.BankKeeper, - app.WasmKeeper, + &app.WasmKeeper, app.StakingKeeper, ) diff --git a/note.json b/note.json deleted file mode 100644 index 24d852c5..00000000 --- a/note.json +++ /dev/null @@ -1,155 +0,0 @@ -{ - "stake": { - "delegator": "cosmos1ph3tq3s5afcg6z4c4mpd9kdklt9a8j7gw8sygn", - "validator": "cosmosvaloper1gtnfjqp6pgkdu6szu5rzk4h5z5d54fadcfsv90", - "stake": { - "denom": "stake", - "amount": "20000000" - }, - "tx_id": 2 - } -} - -[ - Execute { - contract_addr: "cosmos1qg5ega6dykkxc307y25pecuufrjkxkaggkkxh7nad0vhyhtuhw3s6ufdm4", - msg: { - "internal_unstake":{ - "delegator":"cosmos1whqnmupfwm3gfmvjegy0j69z42mfngmg2q8emj", - "validator":"cosmosvaloper1jgaec5l7glsy2l37n25kmqj7p9hrfzsh43df3u", - "amount":{"denom":"stake","amount":"22500000"}} - }, - funds: [] - }, - Execute { - contract_addr: "cosmos1qg5ega6dykkxc307y25pecuufrjkxkaggkkxh7nad0vhyhtuhw3s6ufdm4", - msg: { - "internal_unstake":{ - "delegator":"cosmos1whqnmupfwm3gfmvjegy0j69z42mfngmg2q8emj", - "validator":"cosmosvaloper1mt78w786v2qz24skefvv6x3xulq4495egj33yc", - "amount":{"denom":"stake","amount":"9000000"}}}, funds: [] }] - -panic: -failed to execute scheduled task for contract -"cosmos1xr3rq8yvd7qplsw5yx90ftsr2zdhg4e9z60h5duusgxpv72hud3s493rn8": -execution: {Loading CosmWasm module: execute}: panic [recovered] - -routes: map[ - /cosmos.auth.v1beta1.MsgUpdateParams:0x10378c850 - /cosmos.authz.v1beta1.MsgExec:0x10378c850 - /cosmos.authz.v1beta1.MsgGrant:0x10378c850 - /cosmos.authz.v1beta1.MsgRevoke:0x10378c850 - /cosmos.bank.v1beta1.MsgMultiSend:0x10378c850 - /cosmos.bank.v1beta1.MsgSend:0x10378c850 - /cosmos.bank.v1beta1.MsgSetSendEnabled:0x10378c850 - /cosmos.bank.v1beta1.MsgUpdateParams:0x10378c850 - /cosmos.consensus.v1.MsgUpdateParams:0x10378c850 - /cosmos.crisis.v1beta1.MsgUpdateParams:0x10378c850 - /cosmos.crisis.v1beta1.MsgVerifyInvariant:0x10378c850 - /cosmos.distribution.v1beta1.MsgCommunityPoolSpend:0x10378c850 - /cosmos.distribution.v1beta1.MsgFundCommunityPool:0x10378c850 - /cosmos.distribution.v1beta1.MsgSetWithdrawAddress:0x10378c850 - /cosmos.distribution.v1beta1.MsgUpdateParams:0x10378c850 - /cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward:0x10378c850 / - cosmos.distribution.v1beta1.MsgWithdrawValidatorCommission:0x10378c850 - /cosmos.evidence.v1beta1.MsgSubmitEvidence:0x10378c850 - /cosmos.feegrant.v1beta1.MsgGrantAllowance:0x10378c8 -50 /cosmos.feegrant.v1beta1.MsgRevokeAllowance:0x10378c850 - /cosmos.gov.v1.MsgDeposit:0x10378c850 - /cosmos.gov.v1.MsgExecLegacyContent:0x10378c850 - /cosmos.gov.v1.MsgSubmitProposal:0x10378c850 - /cosmos.gov.v1.MsgUpdateParams:0x10378c850 - /cosmos.gov.v1.MsgVote:0x10378c850 - /cosmos.gov.v1.MsgVoteWeighted:0x10378c850 - /cosmos.gov.v1beta1.MsgDeposit:0x10378c850 - /cosmos.gov.v1beta1.MsgSubmitProposal:0x10378c850 - /cosmos.gov.v1beta1.MsgVote:0x10378c850 - /cosmos.gov.v1beta1.MsgVoteWeighted:0x10378c850 - /cosmos.group.v1.MsgCreateGroup:0x10378c850 - /cosmos.group.v1.MsgCreateGroupPolicy:0x10378c850 - /cosmos.group.v1.MsgCreateGroupWithPolicy:0x10378c850 - /cosmos.group.v1.MsgExec:0x10378c850 - /cosmos.group.v1.MsgLeaveGroup:0x10378c850 - /cosmos.group.v1.MsgSubmitProposal:0x10378c850 - /cosmos.group.v1.MsgUpdateGroupAdmin:0x10378c850 - /cosmos.group.v1.MsgUpdateGroupMembers:0x10378c850 - /cosmos.group.v1.MsgUpdateGroupMetadata:0x10378c850 - /cosmos.group.v1.MsgUpdateGroupPolicyAdmin:0x10378c850 - /cosmos.group.v1.MsgUpdateGroupPolicyDecisionPolicy:0x10378c850 - /cosmos.group.v1.MsgUpdateGroupPolicyMetadata:0x10378c850 - /cosmos.group.v1.MsgVote:0x10378c850 - /cosmos.group.v1.MsgWithdrawProposal:0x10378c850 - /cosmos.mint.v1beta1.MsgUpdateParams:0x10378c850 - /cosmos.nft.v1beta1.MsgSend:0x10378c850 - /cosmos.slashing.v1beta1.MsgUnjail:0x10378c850 - /cosmos.slashing.v1beta1.MsgUpdateParams:0x10378c850 - /cosmos.staking.v1beta1.MsgBeginRedelegate:0x10378c850 - /cosmos.staking.v1beta1.MsgCancelUnbondingDelegation:0x10378c850 - /cosmos.staking.v1beta1.MsgCreateValidator:0x10378c850 - /cosmos.staking.v1beta1.MsgDelegate:0x10378c850 - /cosmos.staking.v1beta1.MsgEditValidator:0x10378c850 - /cosmos.staking.v1beta1.MsgUndelegate:0x10378c850 - /cosmos.staking.v1beta1.MsgUpdateParams:0x10378c850 - /cosmos.upgrade.v1beta1.MsgCancelUpgrade:0x10378c850 - /cosmos.upgrade.v1beta1.MsgSoftwareUpgrade:0x10378c850 - /cosmos.vesting.v1beta1.MsgCreatePeriodicVestingAccount:0x10378c850 - /cosmos.vesting.v1beta1.MsgCreatePermanentLockedAccount:0x10378c850 - /cosmos.vesting.v1beta1.MsgCreateVestingAccount:0x10378c850 - /cosmwasm.wasm.v1.MsgAddCodeUploadParamsAddresses:0x10378c850 - /cosmwasm.wasm.v1.MsgClearAdmin:0x10378c850 - /cosmwasm.wasm.v1.MsgExecuteContract:0x10378c850 - /cosmwasm.wasm.v1.MsgInstantiateContract:0x10378c850 - /cosmwasm.wasm.v1.MsgInstantiateContract2:0x10378c850 - /cosmwasm.wasm.v1.MsgMigrateContract:0x10378c850 - /cosmwasm.wasm.v1.MsgPinCodes:0x10378c850 - /cosmwasm.wasm.v1.MsgRemoveCodeUploadParamsAddresses:0x10378c850 - /cosmwasm.wasm.v1.MsgStoreAndInstantiateContract:0x10378c850 - /cosmwasm.wasm.v1.MsgStoreAndMigrateContract:0x10378c850 - /cosmwasm.wasm.v1.MsgStoreCode:0x10378c850 - /cosmwasm.wasm.v1.MsgSudoContract:0x10378c850 - /cosmwasm.wasm.v1.MsgUnpinCodes:0x10378c850 - /cosmwasm.wasm.v1.MsgUpdateAdmin:0x10378c850 - /cosmwasm.wasm.v1.MsgUpdateContractLabel:0x10378c850 - /cosmwasm.wasm.v1.MsgUpdateInstantiateConfig:0x10378c850 - /cosmwasm.wasm.v1.MsgUpdateParams:0x10378c850 - /ibc.applications.fee.v1.MsgPayPacketFee:0x10378c850 - /ibc.applications.fee.v1.MsgPayPacketFeeAsync:0x10378c850 - /ibc.applications.fee.v1.MsgRegisterCounterpartyPayee:0x10378c850 - /ibc.applications.fee.v1.MsgRegisterPayee:0x10378c850 - /ibc.applications.interchain_accounts.controller.v1.MsgRegisterInterchainAccount:0x10378c850 - /ibc.applications.interchain_accounts.controller.v1.MsgSendTx:0x10378c850 - /ibc.applications.transfer.v1.MsgTransfer:0x10378c850 - /ibc.core.channel.v1.MsgAcknowledgement:0x10378c850 - /ibc.core.channel.v1.MsgChannelCloseConfirm:0x10378c850 - /ibc.core.channel.v1.MsgChannelCloseInit:0x10378c850 - /ibc.core.channel.v1.MsgChannelOpenAck:0x10378c850 - /ibc.core.channel.v1.MsgChannelOpenConfirm:0x10378c850 - /ibc.core.channel.v1.MsgChannelOpenInit:0x10378c850 - /ibc.core.channel.v1.MsgChannelOpenTry:0x10378c850 - /ibc.core.channel.v1.MsgRecvPacket:0x10378c850 - /ibc.core.channel.v1.MsgTimeout:0x10378c850 - /ibc.core.channel.v1.MsgTimeoutOnClose:0x10378c850 - /ibc.core.client.v1.MsgCreateClient:0x10378c850 - /ibc.core.client.v1.MsgSubmitMisbehaviour:0x10378c850 - /ibc.core.client.v1.MsgUpdateClient:0x10378c850 - /ibc.core.client.v1.MsgUpgradeClient:0x10378c850 - /ibc.core.connection.v1.MsgConnectionOpenAck:0x10378c850 - /ibc.core.connection.v1.MsgConnectionOpenConfirm:0x10378c850 - /ibc.core.connection.v1.MsgConnectionOpenInit:0x10378c850 - /ibc.core.connection.v1.MsgConnectionOpenTry:0x10378c850 - /osmosis.meshsecurity.v1beta1.MsgSetVirtualStakingMaxCap:0x10378c850] - - - Cannot unbond 49999999 tokens from validator cosmosvaloper1fhm5lj9wcvxwdxmgtz36mnjuhwtk7ly93c3rqg, not enough staked - - data: { - "internal_unstake":{ - "delegator":"cosmos1yw0saxm7nkwl0e8r3kqpar2a0vusdzcaku8mys", - "validator":"cosmosvaloper1szy96jnddalx7s686e4ylres02uv6a5dyxj82c", - "normalize_amount":{"denom":"stake","amount":"9000000"}, - "inverted_amount":{"denom":"stake","amount":"19999999"} - } - } - - failed to execute message; message index: 0: acknowledge packet callback failed: on ack: submessages: Error calling the VM: Error executing Wasm: Wasmer runtime error: RuntimeError: - Aborted: panicked at contracts/consumer/virtual-staking/src/contract.rs:519:9: \ No newline at end of file diff --git a/tests/e2e/e2e.go b/tests/e2e/e2e.go index 0aca4a83..83f9dda5 100644 --- a/tests/e2e/e2e.go +++ b/tests/e2e/e2e.go @@ -4,7 +4,6 @@ import ( "path/filepath" "testing" - "github.com/CosmWasm/wasmd/x/wasm" "github.com/CosmWasm/wasmd/x/wasm/ibctesting" wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" @@ -38,7 +37,7 @@ func buildPathToWasm(fileName string) string { // NewIBCCoordinator initializes Coordinator with N meshd TestChain instances func NewIBCCoordinator(t *testing.T, n int, opts ...[]wasmkeeper.Option) *ibctesting.Coordinator { return ibctesting.NewCoordinatorX(t, n, - func(t *testing.T, valSet *types.ValidatorSet, genAccs []authtypes.GenesisAccount, chainID string, opts []wasm.Option, balances ...banktypes.Balance) ibctesting.ChainApp { + func(t *testing.T, valSet *types.ValidatorSet, genAccs []authtypes.GenesisAccount, chainID string, opts []wasmkeeper.Option, balances ...banktypes.Balance) ibctesting.ChainApp { return app.SetupWithGenesisValSet(t, valSet, genAccs, chainID, opts, balances...) }, opts..., diff --git a/tests/e2e/slashing_test.go b/tests/e2e/slashing_test.go index 31bcba4b..af3152f0 100644 --- a/tests/e2e/slashing_test.go +++ b/tests/e2e/slashing_test.go @@ -284,3 +284,66 @@ func TestSlashingScenario3(t *testing.T) { // Check new free collateral require.Equal(t, 0, providerCli.QueryVaultFreeBalance()) // 185 - max(32, 185) = 185 - 185 = 0 } + +func TestSlasingImmediateUnbond(t *testing.T) { + x := setupExampleChains(t) + _, _, providerCli := setupMeshSecurity(t, x) + + // Provider chain + // ============== + // Deposit - A user deposits the vault denom to provide some collateral to their account + execMsg := fmt.Sprintf(`{"bond":{"amount":{"denom":"%s", "amount":"200000000"}}}`, x.ProviderDenom) + providerCli.MustExecVault(execMsg) + + // Stake Locally - A user triggers a local staking action to a chosen validator. + myLocalValidatorAddr := sdk.ValAddress(x.ProviderChain.Vals.Validators[0].Address).String() + execLocalStakingMsg := fmt.Sprintf(`{"stake_local":{"amount": {"denom":%q, "amount":"%d"}, "msg":%q}}`, + x.ProviderDenom, 100_000_000, + base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf(`{"validator": "%s"}`, myLocalValidatorAddr)))) + providerCli.MustExecVault(execLocalStakingMsg) + + assert.Equal(t, 100_000_000, providerCli.QueryVaultFreeBalance()) + + // Check slashable amount + require.Equal(t, 20_000_000, providerCli.QuerySlashableAmount()) + // Check free collateral + require.Equal(t, 100_000_000, providerCli.QueryVaultFreeBalance()) + + // Validator on the provider chain is jailed + myLocalValidatorConsAddr := sdk.ConsAddress(x.ProviderChain.Vals.Validators[0].PubKey.Address()) + jailValidator(t, myLocalValidatorConsAddr, x.Coordinator, x.ProviderChain, x.ProviderApp) + + x.ProviderChain.NextBlock() + + // Check new collateral + require.Equal(t, 200_000_000, providerCli.QueryVaultBalance()) + // Check new max lien + require.Equal(t, 100_000_000, providerCli.QueryMaxLien()) + // Check new slashable amount + require.Equal(t, 20_000_000, providerCli.QuerySlashableAmount()) + // Check new free collateral + require.Equal(t, 100_000_000, providerCli.QueryVaultFreeBalance()) + + // Get native staking proxy contract + nativeStakingProxy := providerCli.QueryNativeStakingProxyByOwner(x.ProviderChain.SenderAccount.GetAddress().String()) + + execMsg = fmt.Sprintf(`{"unstake": {"validator":%q,"amount": {"denom":%q, "amount":"%d"}}}`, + myLocalValidatorAddr, x.ProviderDenom, 10_000_000) + _, err := providerCli.Exec(nativeStakingProxy, execMsg) + require.NoError(t, err) + + x.ProviderChain.NextBlock() + + _, err = providerCli.Exec(nativeStakingProxy, `{"release_unbonded": {}}`) + require.NoError(t, err) + + // Check new collateral + require.Equal(t, 200_000_000, providerCli.QueryVaultBalance()) + // Check new max lien + // Max lien decrease as release_unbonded + require.Equal(t, 90_000_001, providerCli.QueryMaxLien()) + // Check new slashable amount + require.Equal(t, 18000001, providerCli.QuerySlashableAmount()) + // Check new free collateral + require.Equal(t, 109999999, providerCli.QueryVaultFreeBalance()) +} diff --git a/tests/e2e/test_client.go b/tests/e2e/test_client.go index cf3d7e26..ee124aa2 100644 --- a/tests/e2e/test_client.go +++ b/tests/e2e/test_client.go @@ -27,6 +27,7 @@ import ( "github.com/osmosis-labs/mesh-security-sdk/x/meshsecurity" "github.com/osmosis-labs/mesh-security-sdk/x/meshsecurity/keeper" "github.com/osmosis-labs/mesh-security-sdk/x/meshsecurity/types" + providertypes "github.com/osmosis-labs/mesh-security-sdk/x/meshsecurityprovider/types" ) // Query is a query type used in tests only @@ -129,6 +130,7 @@ func (tc *TestChain) SendMsgsWithSigner(privKey cryptotypes.PrivKey, signer *aut type ProviderContracts struct { Vault sdk.AccAddress + NativeStaking sdk.AccAddress ExternalStaking sdk.AccAddress } @@ -153,6 +155,7 @@ func (p *TestProviderClient) BootstrapContracts(provApp *app.MeshApp, connId, po params := provApp.MeshSecProvKeeper.GetParams(ctx) params.VaultAddress = vaultContract.String() provApp.MeshSecProvKeeper.SetParams(ctx, params) + // external staking extStakingCodeID := p.chain.StoreCodeFile(buildPathToWasm("mesh_external_staking.wasm")).CodeID initMsg = []byte(fmt.Sprintf( @@ -165,6 +168,19 @@ func (p *TestProviderClient) BootstrapContracts(provApp *app.MeshApp, connId, po ExternalStaking: externalStakingContract, } p.Contracts = r + + // local staking + vaultConfig := p.QueryVault(Query{ + "config": {}, + }) + require.Contains(p.t, vaultConfig, "local_staking") + nativeStaking, err := sdk.AccAddressFromBech32(vaultConfig["local_staking"].(string)) + require.NoError(p.t, err) + r.NativeStaking = nativeStaking + p.Contracts = r + + p.MustExecParamsChangeProposal(provApp, vaultContract.String(), nativeStaking.String()) + return r } @@ -240,6 +256,19 @@ func (p TestProviderClient) ExecWithSigner(privKey cryptotypes.PrivKey, signer * return rsp, err } +// MustExecGovProposal submit and vote yes on proposal +func (p TestProviderClient) MustExecParamsChangeProposal(provApp *app.MeshApp, vault, nativeStaking string) { + msg := &providertypes.MsgUpdateParams{ + Authority: provApp.MeshSecKeeper.GetAuthority(), + Params: providertypes.Params{ + VaultAddress: vault, + NativeStakingAddress: nativeStaking, + }, + } + proposalID := submitGovProposal(p.t, p.chain, msg) + voteAndPassGovProposal(p.t, p.chain, proposalID) +} + func (p TestProviderClient) MustFailExecVault(payload string, funds ...sdk.Coin) error { rsp, err := p.Exec(p.Contracts.Vault, payload, funds...) require.Error(p.t, err, "Response: %v", rsp) @@ -270,6 +299,18 @@ func (p TestProviderClient) QueryExtStakingAmount(user, validator string) int { return ParseHighLow(p.t, qRsp["stake"]).Low } +func (p TestProviderClient) QueryNativeStakingProxyByOwner(user string) sdk.AccAddress { + qRsp := p.QueryNativeStaking(Query{ + "proxy_by_owner": { + "owner": user, + }, + }) + require.Contains(p.t, qRsp, "proxy") + a, err := sdk.AccAddressFromBech32(qRsp["proxy"].(string)) + require.NoError(p.t, err) + + return a +} func (p TestProviderClient) QueryExtStaking(q Query) QueryResponse { return Querier(p.t, p.chain)(p.Contracts.ExternalStaking.String(), q) } @@ -278,6 +319,10 @@ func (p TestProviderClient) QueryVault(q Query) QueryResponse { return Querier(p.t, p.chain)(p.Contracts.Vault.String(), q) } +func (p TestProviderClient) QueryNativeStaking(q Query) QueryResponse { + return Querier(p.t, p.chain)(p.Contracts.NativeStaking.String(), q) +} + type HighLowType struct { High, Low int } diff --git a/tests/starship/setup/setup.go b/tests/starship/setup/setup.go index f0046762..656c0dd1 100644 --- a/tests/starship/setup/setup.go +++ b/tests/starship/setup/setup.go @@ -127,7 +127,7 @@ func (p *ProviderClient) BootstrapContracts(connId, portID, rewardDenom string) nativeInitMsg := []byte(fmt.Sprintf(`{"denom": %q, "proxy_code_id": %d, "slash_ratio_dsign": %q, "slash_ratio_offline": %q }`, localTokenDenom, proxyCodeID, localSlashRatioDoubleSign, localSlashRatioOffline)) initMsg := []byte(fmt.Sprintf(`{"denom": %q, "local_staking": {"code_id": %d, "msg": %q}}`, localTokenDenom, nativeStakingCodeID, base64.StdEncoding.EncodeToString(nativeInitMsg))) - contracts, err := InstantiateContract(p.Chain, vaultCodeID, "provider-valut-contract", initMsg) + contracts, err := InstantiateContract(p.Chain, vaultCodeID, "provider-vault-contract", initMsg) if err != nil { return nil, err } diff --git a/tests/testdata/mesh_converter.wasm.gz b/tests/testdata/mesh_converter.wasm.gz index 61821da4..a41ea36a 100644 Binary files a/tests/testdata/mesh_converter.wasm.gz and b/tests/testdata/mesh_converter.wasm.gz differ diff --git a/tests/testdata/mesh_external_staking.wasm.gz b/tests/testdata/mesh_external_staking.wasm.gz index 4ba8bf8b..7ae90b5b 100644 Binary files a/tests/testdata/mesh_external_staking.wasm.gz and b/tests/testdata/mesh_external_staking.wasm.gz differ diff --git a/tests/testdata/mesh_native_staking.wasm.gz b/tests/testdata/mesh_native_staking.wasm.gz index 1670dbf4..aab4a86e 100644 Binary files a/tests/testdata/mesh_native_staking.wasm.gz and b/tests/testdata/mesh_native_staking.wasm.gz differ diff --git a/tests/testdata/mesh_native_staking_proxy.wasm.gz b/tests/testdata/mesh_native_staking_proxy.wasm.gz index 04f30997..a702a620 100644 Binary files a/tests/testdata/mesh_native_staking_proxy.wasm.gz and b/tests/testdata/mesh_native_staking_proxy.wasm.gz differ diff --git a/tests/testdata/mesh_osmosis_price_provider.wasm.gz b/tests/testdata/mesh_osmosis_price_provider.wasm.gz index 0cdd504d..ff1a327e 100644 Binary files a/tests/testdata/mesh_osmosis_price_provider.wasm.gz and b/tests/testdata/mesh_osmosis_price_provider.wasm.gz differ diff --git a/tests/testdata/mesh_remote_price_feed.wasm.gz b/tests/testdata/mesh_remote_price_feed.wasm.gz index b5be67c2..8d30589e 100644 Binary files a/tests/testdata/mesh_remote_price_feed.wasm.gz and b/tests/testdata/mesh_remote_price_feed.wasm.gz differ diff --git a/tests/testdata/mesh_simple_price_feed.wasm.gz b/tests/testdata/mesh_simple_price_feed.wasm.gz index 97a29e25..9a2aca20 100644 Binary files a/tests/testdata/mesh_simple_price_feed.wasm.gz and b/tests/testdata/mesh_simple_price_feed.wasm.gz differ diff --git a/tests/testdata/mesh_vault.wasm.gz b/tests/testdata/mesh_vault.wasm.gz index 8ff46c34..fb470f66 100644 Binary files a/tests/testdata/mesh_vault.wasm.gz and b/tests/testdata/mesh_vault.wasm.gz differ diff --git a/tests/testdata/mesh_virtual_staking.wasm.gz b/tests/testdata/mesh_virtual_staking.wasm.gz index d2ffc9f4..69e5bb42 100644 Binary files a/tests/testdata/mesh_virtual_staking.wasm.gz and b/tests/testdata/mesh_virtual_staking.wasm.gz differ diff --git a/tests/testdata/version.txt b/tests/testdata/version.txt index 7228d975..94427d4e 100644 --- a/tests/testdata/version.txt +++ b/tests/testdata/version.txt @@ -1 +1 @@ -ef0e3840b092ed66cd0968e2b9b253ae243ffe52 +d9e27c854aaf3bb604d32f5fa8e6c3e42b686edf diff --git a/x/meshsecurityprovider/contract/in_message.go b/x/meshsecurityprovider/contract/in_message.go index 37ac6a03..3cd5348a 100644 --- a/x/meshsecurityprovider/contract/in_message.go +++ b/x/meshsecurityprovider/contract/in_message.go @@ -7,8 +7,9 @@ type ( Provider *ProviderMsg `json:"provider,omitempty"` } ProviderMsg struct { - Bond *BondMsg `json:"bond,omitempty"` - Unbond *UnbondMsg `json:"unbond,omitempty"` + Bond *BondMsg `json:"bond,omitempty"` + Unbond *UnbondMsg `json:"unbond,omitempty"` + Unstake *UnstakeMsg `json:"unstake,omitempty"` } BondMsg struct { Amount wasmvmtypes.Coin `json:"amount"` @@ -18,4 +19,9 @@ type ( Amount wasmvmtypes.Coin `json:"amount"` Delegator string `json:"delegator"` } + UnstakeMsg struct { + Amount wasmvmtypes.Coin `json:"amount"` + Validator string `json:"validator"` + Delegator string `json:"delegator"` + } ) diff --git a/x/meshsecurityprovider/keeper/handle_plugin.go b/x/meshsecurityprovider/keeper/handle_plugin.go index 55358da2..923d4fa2 100644 --- a/x/meshsecurityprovider/keeper/handle_plugin.go +++ b/x/meshsecurityprovider/keeper/handle_plugin.go @@ -45,7 +45,10 @@ func (h CustomMessenger) DispatchMsg(ctx sdk.Context, contractAddr sdk.AccAddres return h.k.HandleBondMsg(ctx, contractAddr, customMsg.Provider.Bond) case customMsg.Provider.Unbond != nil: return h.k.HandleUnbondMsg(ctx, contractAddr, customMsg.Provider.Unbond) + case customMsg.Provider.Unstake != nil: + return h.k.HandleUnstakeMsg(ctx, contractAddr, customMsg.Provider.Unstake) } + return nil, nil, wasmtypes.ErrUnknownMsg } diff --git a/x/meshsecurityprovider/keeper/keeper.go b/x/meshsecurityprovider/keeper/keeper.go index ed90d183..9eda5798 100644 --- a/x/meshsecurityprovider/keeper/keeper.go +++ b/x/meshsecurityprovider/keeper/keeper.go @@ -1,6 +1,9 @@ package keeper import ( + "encoding/json" + "fmt" + "github.com/cometbft/cometbft/libs/log" wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" @@ -8,6 +11,7 @@ import ( storetypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" "github.com/osmosis-labs/mesh-security-sdk/x/meshsecurityprovider/contract" "github.com/osmosis-labs/mesh-security-sdk/x/meshsecurityprovider/types" @@ -147,3 +151,104 @@ func (k Keeper) HandleUnbondMsg(ctx sdk.Context, actor sdk.AccAddress, unbondMsg sdk.NewAttribute(types.AttributeKeyDelegator, delAddr.String()), )}, nil, nil } + +func (k Keeper) HandleUnstakeMsg(ctx sdk.Context, actor sdk.AccAddress, unstakeMsg *contract.UnstakeMsg) ([]sdk.Event, [][]byte, error) { + nativeContract := k.NativeStakingAddress(ctx) + nativeContractAddr, err := sdk.AccAddressFromBech32(nativeContract) + if err != nil { + return nil, nil, sdkerrors.ErrInvalidAddress.Wrapf("native staking contract not able to get") + } + var proxyRes types.ProxyByOwnerResponse + + resBytes, err := k.wasmKeeper.QuerySmart( + ctx, + nativeContractAddr, + []byte(fmt.Sprintf(`{"proxy_by_owner": {"owner": "%s"}}`, unstakeMsg.Delegator)), + ) + + if err != nil { + return nil, nil, sdkerrors.ErrUnauthorized.Wrapf("contract has no permission for mesh security operations") + } + if err = json.Unmarshal(resBytes, &proxyRes); err != nil { + return nil, nil, sdkerrors.ErrUnauthorized.Wrapf("contract has no permission for mesh security operations") + } + if proxyRes.Proxy == "" { + return nil, nil, sdkerrors.ErrUnauthorized.Wrapf("contract has no permission for mesh security operations") + } + + proxyContract, err := sdk.AccAddressFromBech32(proxyRes.Proxy) + if err != nil { + return nil, nil, sdkerrors.ErrInvalidAddress.Wrapf("native staking proxy contract not able to get") + } + + coin, err := wasmkeeper.ConvertWasmCoinToSdkCoin(unstakeMsg.Amount) + if err != nil { + return nil, nil, err + } + + valAddr, err := sdk.ValAddressFromBech32(unstakeMsg.Validator) + if err != nil { + return nil, nil, err + } + + err = k.unstake(ctx, proxyContract, valAddr, coin) + if err != nil { + return nil, nil, err + } + + return []sdk.Event{sdk.NewEvent( + types.EventTypeUnstake, + sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName), + sdk.NewAttribute(sdk.AttributeKeyAmount, coin.String()), + sdk.NewAttribute(types.AttributeKeyValidator, valAddr.String()), + )}, nil, nil +} + +func (k Keeper) unstake(ctx sdk.Context, proxyContract sdk.AccAddress, validator sdk.ValAddress, coin sdk.Coin) error { + if coin.Amount.IsNil() || coin.Amount.IsZero() || coin.Amount.IsNegative() { + return sdkerrors.ErrInvalidRequest.Wrap("amount") + } + + // Ensure staking constraints + bondDenom := k.stakingKeeper.BondDenom(ctx) + if coin.Denom != bondDenom { + return sdkerrors.ErrInvalidRequest.Wrapf("invalid coin denomination: got %s, expected %s", coin.Denom, bondDenom) + } + + validatorInfo, found := k.stakingKeeper.GetValidator(ctx, validator) + if !found { + return sdkerrors.ErrNotFound.Wrapf("can not found validator with address: %s", validator.String()) + } + + shares, err := k.stakingKeeper.ValidateUnbondAmount(ctx, proxyContract, validator, coin.Amount) + if err == stakingtypes.ErrNoDelegation { + return nil + } else if err != nil { + return err + } + + if validatorInfo.IsBonded() { + _, err = k.stakingKeeper.Undelegate(ctx, proxyContract, validator, shares) + if err != nil { + return err + } + + return nil + } + + return k.InstantUndelegate(ctx, proxyContract, validator, shares) +} + +func (k Keeper) InstantUndelegate(ctx sdk.Context, delAddr sdk.AccAddress, validator sdk.ValAddress, sharesAmount sdk.Dec) error { + returnAmount, err := k.stakingKeeper.Unbond(ctx, delAddr, validator, sharesAmount) + if err != nil { + return err + } + + bondDenom := k.stakingKeeper.BondDenom(ctx) + + amt := sdk.NewCoin(bondDenom, returnAmount) + res := sdk.NewCoins(amt) + + return k.bankKeeper.UndelegateCoinsFromModuleToAccount(ctx, stakingtypes.NotBondedPoolName, delAddr, res) +} diff --git a/x/meshsecurityprovider/module.go b/x/meshsecurityprovider/module.go index ae573e35..5b59b416 100644 --- a/x/meshsecurityprovider/module.go +++ b/x/meshsecurityprovider/module.go @@ -73,7 +73,7 @@ type AppModule struct { } func (am AppModule) RegisterServices(cfg module.Configurator) { - // types.RegisterMsgServer(cfg.MsgServer(), meshsecurityprovider.NewMsgServerImpl(&am.k)) + types.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServerImpl(am.k)) // queryproto.RegisterQueryServer(cfg.QueryServer(), grpc.Querier{Q: module.NewQuerier(am.k)}) } diff --git a/x/meshsecurityprovider/types/codec.go b/x/meshsecurityprovider/types/codec.go index 76e10469..ff0221a6 100644 --- a/x/meshsecurityprovider/types/codec.go +++ b/x/meshsecurityprovider/types/codec.go @@ -3,9 +3,9 @@ package types import ( "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/codec/types" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/msgservice" - authzcodec "github.com/cosmos/cosmos-sdk/x/authz/codec" ) // RegisterLegacyAminoCodec registers the necessary x/meshsecurityprovider interfaces and concrete types @@ -30,10 +30,6 @@ var ( func init() { RegisterLegacyAminoCodec(amino) - // Register all Amino interfaces and concrete types on the authz Amino codec so that this can later be - // used to properly serialize MsgGrant and MsgExec instances + cryptocodec.RegisterCrypto(amino) sdk.RegisterLegacyAminoCodec(amino) - RegisterLegacyAminoCodec(authzcodec.Amino) - - amino.Seal() } diff --git a/x/meshsecurityprovider/types/keys.go b/x/meshsecurityprovider/types/keys.go index f45acf39..20edcaa5 100644 --- a/x/meshsecurityprovider/types/keys.go +++ b/x/meshsecurityprovider/types/keys.go @@ -1,7 +1,7 @@ package types const ( - ModuleName = "test_module" + ModuleName = "mesh-security-provider" StoreKey = ModuleName @@ -9,7 +9,7 @@ const ( ) var ( - // Key defines the store key for test_module. + // Key defines the store key for mesh security provider module. Key = []byte{0x01} ParamsKey = []byte{0x02} )