From 3d05c50f67fc22cfdeed388c40ebfb4fcdf5b650 Mon Sep 17 00:00:00 2001 From: "Tony Arcieri (iqlusion)" Date: Mon, 7 Nov 2022 18:33:26 -0700 Subject: [PATCH] cosmrs: refactor into submodules (#303) Factors code out of toplevel modules (which map 1:1 to the Go modules) into type-specific submodules. This makes it easier to locate the definition of a specific type by filename, and generally makes it easier to peruse the codebase. --- cosmrs/src/abci.rs | 130 +-- cosmrs/src/abci/gas_info.rs | 33 + cosmrs/src/abci/msg_data.rs | 93 +++ cosmrs/src/auth.rs | 86 +- cosmrs/src/auth/base_account.rs | 52 ++ cosmrs/src/auth/module_account.rs | 37 + cosmrs/src/bank.rs | 203 +---- cosmrs/src/bank/msg_multi_send.rs | 58 ++ cosmrs/src/bank/msg_send.rs | 58 ++ cosmrs/src/bank/multi_send_io.rs | 87 ++ cosmrs/src/base.rs | 283 +------ cosmrs/src/base/account_id.rs | 160 ++++ cosmrs/src/base/coin.rs | 75 ++ cosmrs/src/base/denom.rs | 62 ++ cosmrs/src/cosmwasm.rs | 748 +----------------- cosmrs/src/cosmwasm/absolute_tx_position.rs | 33 + cosmrs/src/cosmwasm/access_config.rs | 49 ++ cosmrs/src/cosmwasm/code_info_response.rs | 37 + .../cosmwasm/contract_code_history_entry.rs | 41 + cosmrs/src/cosmwasm/contract_info.rs | 62 ++ cosmrs/src/cosmwasm/msg_clear_admin.rs | 73 ++ cosmrs/src/cosmwasm/msg_execute_contract.rs | 81 ++ cosmrs/src/cosmwasm/msg_initiate_contract.rs | 108 +++ cosmrs/src/cosmwasm/msg_migrate_contract.rs | 78 ++ cosmrs/src/cosmwasm/msg_store_code.rs | 76 ++ cosmrs/src/cosmwasm/msg_update_admin.rs | 78 ++ cosmrs/src/cosmwasm/query_code_response.rs | 32 + cosmrs/src/distribution.rs | 264 +------ .../distribution/msg_fund_community_pool.rs | 71 ++ .../distribution/msg_set_withdraw_address.rs | 61 ++ .../msg_withdraw_delegator_reward.rs | 65 ++ .../msg_withdraw_validator_commission.rs | 60 ++ cosmrs/src/feegrant.rs | 316 +------- cosmrs/src/feegrant/allowed_msg_allowance.rs | 61 ++ cosmrs/src/feegrant/basic_allowance.rs | 63 ++ cosmrs/src/feegrant/msg_grant_allowance.rs | 59 ++ cosmrs/src/feegrant/msg_revoke_allowance.rs | 53 ++ cosmrs/src/feegrant/periodic_allowance.rs | 75 ++ cosmrs/src/staking.rs | 216 +---- cosmrs/src/staking/msg_begin_redelegate.rs | 76 ++ cosmrs/src/staking/msg_delegate.rs | 67 ++ cosmrs/src/staking/msg_undelegate.rs | 67 ++ cosmrs/src/vesting.rs | 273 +------ cosmrs/src/vesting/base_vesting_account.rs | 73 ++ .../src/vesting/continuous_vesting_account.rs | 41 + cosmrs/src/vesting/delayed_vesting_account.rs | 35 + cosmrs/src/vesting/period.rs | 35 + .../src/vesting/periodic_vesting_account.rs | 53 ++ .../src/vesting/permanent_locked_account.rs | 36 + 49 files changed, 2599 insertions(+), 2434 deletions(-) create mode 100644 cosmrs/src/abci/gas_info.rs create mode 100644 cosmrs/src/abci/msg_data.rs create mode 100644 cosmrs/src/auth/base_account.rs create mode 100644 cosmrs/src/auth/module_account.rs create mode 100644 cosmrs/src/bank/msg_multi_send.rs create mode 100644 cosmrs/src/bank/msg_send.rs create mode 100644 cosmrs/src/bank/multi_send_io.rs create mode 100644 cosmrs/src/base/account_id.rs create mode 100644 cosmrs/src/base/coin.rs create mode 100644 cosmrs/src/base/denom.rs create mode 100644 cosmrs/src/cosmwasm/absolute_tx_position.rs create mode 100644 cosmrs/src/cosmwasm/access_config.rs create mode 100644 cosmrs/src/cosmwasm/code_info_response.rs create mode 100644 cosmrs/src/cosmwasm/contract_code_history_entry.rs create mode 100644 cosmrs/src/cosmwasm/contract_info.rs create mode 100644 cosmrs/src/cosmwasm/msg_clear_admin.rs create mode 100644 cosmrs/src/cosmwasm/msg_execute_contract.rs create mode 100644 cosmrs/src/cosmwasm/msg_initiate_contract.rs create mode 100644 cosmrs/src/cosmwasm/msg_migrate_contract.rs create mode 100644 cosmrs/src/cosmwasm/msg_store_code.rs create mode 100644 cosmrs/src/cosmwasm/msg_update_admin.rs create mode 100644 cosmrs/src/cosmwasm/query_code_response.rs create mode 100644 cosmrs/src/distribution/msg_fund_community_pool.rs create mode 100644 cosmrs/src/distribution/msg_set_withdraw_address.rs create mode 100644 cosmrs/src/distribution/msg_withdraw_delegator_reward.rs create mode 100644 cosmrs/src/distribution/msg_withdraw_validator_commission.rs create mode 100644 cosmrs/src/feegrant/allowed_msg_allowance.rs create mode 100644 cosmrs/src/feegrant/basic_allowance.rs create mode 100644 cosmrs/src/feegrant/msg_grant_allowance.rs create mode 100644 cosmrs/src/feegrant/msg_revoke_allowance.rs create mode 100644 cosmrs/src/feegrant/periodic_allowance.rs create mode 100644 cosmrs/src/staking/msg_begin_redelegate.rs create mode 100644 cosmrs/src/staking/msg_delegate.rs create mode 100644 cosmrs/src/staking/msg_undelegate.rs create mode 100644 cosmrs/src/vesting/base_vesting_account.rs create mode 100644 cosmrs/src/vesting/continuous_vesting_account.rs create mode 100644 cosmrs/src/vesting/delayed_vesting_account.rs create mode 100644 cosmrs/src/vesting/period.rs create mode 100644 cosmrs/src/vesting/periodic_vesting_account.rs create mode 100644 cosmrs/src/vesting/permanent_locked_account.rs diff --git a/cosmrs/src/abci.rs b/cosmrs/src/abci.rs index 4b6e7411..c26daa32 100644 --- a/cosmrs/src/abci.rs +++ b/cosmrs/src/abci.rs @@ -1,128 +1,12 @@ -//! Abci-related functionality +//! Application/BlockChain Interface (ABCI)-related functionality. -use crate::{ - proto::{self, traits::Message}, - tx::Msg, - ErrorReport, Gas, Result, +mod gas_info; +mod msg_data; + +pub use self::{ + gas_info::GasInfo, + msg_data::{MsgData, TxMsgData}, }; -use serde::{Deserialize, Serialize}; /// Transaction data. pub type Data = Vec; - -/// MsgData defines the data returned in a Result object during message execution. -#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] -pub struct MsgData { - /// Incoming message type that emitted this result data, for example `"/cosmos.bank.v1beta1.MsgSend"`. - pub msg_type: String, - - /// Binary data emitted by this message. - // Do note that usually the data has to be decoded into the corresponding protobuf `Response` type. - // For example, if the data was emitted as a result of a `MsgSend`, i.e. `msg.msg_type == "/cosmos.bank.v1beta1.MsgSend"`, - // then you should decode it into `"/cosmos.bank.v1beta1.MsgSendResponse" - pub data: Vec, -} - -impl MsgData { - /// Attempts to decode the `data` field of this result into the specified `Msg` type. - pub fn try_decode_as(&self) -> Result { - M::Proto::decode(&*self.data)?.try_into() - } -} - -impl Msg for MsgData { - type Proto = proto::cosmos::base::abci::v1beta1::MsgData; -} - -impl TryFrom for MsgData { - type Error = ErrorReport; - - fn try_from(proto: proto::cosmos::base::abci::v1beta1::MsgData) -> Result { - Ok(MsgData { - msg_type: proto.msg_type, - data: proto.data, - }) - } -} - -impl From for proto::cosmos::base::abci::v1beta1::MsgData { - fn from(msg_data: MsgData) -> Self { - proto::cosmos::base::abci::v1beta1::MsgData { - msg_type: msg_data.msg_type, - data: msg_data.data, - } - } -} - -/// TxMsgData defines a list of MsgData. A transaction will have a MsgData object for each message. -#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] -pub struct TxMsgData { - /// Data emitted by the messages in a particular transaction. - // Note: this field will be deprecated and not populated as of cosmos-sdk 0.46. - // It will be superseded by `msg_responses` field of type Vec - pub data: Vec, -} - -impl TryFrom for TxMsgData { - type Error = ErrorReport; - - fn try_from(data: Data) -> Result { - proto::cosmos::base::abci::v1beta1::TxMsgData::decode(data.as_ref())?.try_into() - } -} - -impl Msg for TxMsgData { - type Proto = proto::cosmos::base::abci::v1beta1::TxMsgData; -} - -impl TryFrom for TxMsgData { - type Error = ErrorReport; - - fn try_from(proto: proto::cosmos::base::abci::v1beta1::TxMsgData) -> Result { - Ok(TxMsgData { - data: proto - .data - .into_iter() - .map(TryFrom::try_from) - .collect::>()?, - }) - } -} - -impl From for proto::cosmos::base::abci::v1beta1::TxMsgData { - fn from(tx_msg_data: TxMsgData) -> Self { - proto::cosmos::base::abci::v1beta1::TxMsgData { - data: tx_msg_data.data.into_iter().map(Into::into).collect(), - } - } -} - -/// GasInfo defines tx execution gas context. -#[derive(Copy, Clone, Debug, Serialize, Deserialize, Default, Eq, PartialEq)] -pub struct GasInfo { - /// GasWanted is the maximum units of work we allow this tx to perform. - pub gas_wanted: Gas, - - /// GasUsed is the amount of gas actually consumed. - pub gas_used: Gas, -} - -impl TryFrom for GasInfo { - type Error = ErrorReport; - - fn try_from(proto: proto::cosmos::base::abci::v1beta1::GasInfo) -> Result { - Ok(GasInfo { - gas_wanted: proto.gas_wanted, - gas_used: proto.gas_used, - }) - } -} - -impl From for proto::cosmos::base::abci::v1beta1::GasInfo { - fn from(info: GasInfo) -> Self { - proto::cosmos::base::abci::v1beta1::GasInfo { - gas_wanted: info.gas_wanted, - gas_used: info.gas_wanted, - } - } -} diff --git a/cosmrs/src/abci/gas_info.rs b/cosmrs/src/abci/gas_info.rs new file mode 100644 index 00000000..7be2d522 --- /dev/null +++ b/cosmrs/src/abci/gas_info.rs @@ -0,0 +1,33 @@ +use crate::{proto, ErrorReport, Gas, Result}; +use serde::{Deserialize, Serialize}; + +/// [`GasInfo`] defines constraints for how much gas to use to execute a +/// transaction. +#[derive(Copy, Clone, Debug, Serialize, Deserialize, Default, Eq, PartialEq)] +pub struct GasInfo { + /// GasWanted is the maximum units of work we allow this tx to perform. + pub gas_wanted: Gas, + + /// GasUsed is the amount of gas actually consumed. + pub gas_used: Gas, +} + +impl TryFrom for GasInfo { + type Error = ErrorReport; + + fn try_from(proto: proto::cosmos::base::abci::v1beta1::GasInfo) -> Result { + Ok(GasInfo { + gas_wanted: proto.gas_wanted, + gas_used: proto.gas_used, + }) + } +} + +impl From for proto::cosmos::base::abci::v1beta1::GasInfo { + fn from(info: GasInfo) -> Self { + proto::cosmos::base::abci::v1beta1::GasInfo { + gas_wanted: info.gas_wanted, + gas_used: info.gas_wanted, + } + } +} diff --git a/cosmrs/src/abci/msg_data.rs b/cosmrs/src/abci/msg_data.rs new file mode 100644 index 00000000..05c6d705 --- /dev/null +++ b/cosmrs/src/abci/msg_data.rs @@ -0,0 +1,93 @@ +use super::Data; +use crate::{ + proto::{self, traits::Message}, + tx::Msg, + ErrorReport, Result, +}; + +/// MsgData defines the data returned in a Result object during message execution. +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +pub struct MsgData { + /// Incoming message type that emitted this result data, for example `"/cosmos.bank.v1beta1.MsgSend"`. + pub msg_type: String, + + /// Binary data emitted by this message. + // Do note that usually the data has to be decoded into the corresponding protobuf `Response` type. + // For example, if the data was emitted as a result of a `MsgSend`, i.e. `msg.msg_type == "/cosmos.bank.v1beta1.MsgSend"`, + // then you should decode it into `"/cosmos.bank.v1beta1.MsgSendResponse" + pub data: Vec, +} + +impl MsgData { + /// Attempts to decode the `data` field of this result into the specified `Msg` type. + pub fn try_decode_as(&self) -> Result { + M::Proto::decode(&*self.data)?.try_into() + } +} + +impl Msg for MsgData { + type Proto = proto::cosmos::base::abci::v1beta1::MsgData; +} + +impl TryFrom for MsgData { + type Error = ErrorReport; + + fn try_from(proto: proto::cosmos::base::abci::v1beta1::MsgData) -> Result { + Ok(MsgData { + msg_type: proto.msg_type, + data: proto.data, + }) + } +} + +impl From for proto::cosmos::base::abci::v1beta1::MsgData { + fn from(msg_data: MsgData) -> Self { + proto::cosmos::base::abci::v1beta1::MsgData { + msg_type: msg_data.msg_type, + data: msg_data.data, + } + } +} + +/// TxMsgData defines a list of MsgData. A transaction will have a MsgData object for each message. +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +pub struct TxMsgData { + /// Data emitted by the messages in a particular transaction. + // Note: this field will be deprecated and not populated as of cosmos-sdk 0.46. + // It will be superseded by `msg_responses` field of type Vec + pub data: Vec, +} + +impl TryFrom for TxMsgData { + type Error = ErrorReport; + + fn try_from(data: Data) -> Result { + proto::cosmos::base::abci::v1beta1::TxMsgData::decode(data.as_ref())?.try_into() + } +} + +impl Msg for TxMsgData { + type Proto = proto::cosmos::base::abci::v1beta1::TxMsgData; +} + +impl TryFrom for TxMsgData { + type Error = ErrorReport; + + fn try_from(proto: proto::cosmos::base::abci::v1beta1::TxMsgData) -> Result { + Ok(TxMsgData { + data: proto + .data + .into_iter() + .map(TryFrom::try_from) + .collect::>()?, + }) + } +} + +impl From for proto::cosmos::base::abci::v1beta1::TxMsgData { + fn from(tx_msg_data: TxMsgData) -> Self { + proto::cosmos::base::abci::v1beta1::TxMsgData { + data: tx_msg_data.data.into_iter().map(Into::into).collect(), + } + } +} diff --git a/cosmrs/src/auth.rs b/cosmrs/src/auth.rs index c52e4f4a..d0e3e7d0 100644 --- a/cosmrs/src/auth.rs +++ b/cosmrs/src/auth.rs @@ -1,84 +1,6 @@ -//! Auth functionality. +//! Authentication module: AuthN-related functionality. -use crate::crypto::PublicKey; -use crate::tx::{AccountNumber, SequenceNumber}; -use crate::Result; -use crate::{proto, AccountId, ErrorReport}; +mod base_account; +mod module_account; -/// BaseAccount defines a base account type. It contains all the necessary fields -/// for basic account functionality. Any custom account type should extend this -/// type for additional functionality (e.g. vesting). -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct BaseAccount { - /// Bech32 [`AccountId`] of this account. - pub address: AccountId, - - /// Optional [`PublicKey`] associated with this account. - pub pubkey: Option, - - /// `account_number` is the account number of the account in state - pub account_number: AccountNumber, - - /// Sequence of the account, which describes the number of committed transactions signed by a - /// given address. - pub sequence: SequenceNumber, -} - -impl TryFrom for BaseAccount { - type Error = ErrorReport; - - fn try_from(proto: proto::cosmos::auth::v1beta1::BaseAccount) -> Result { - Ok(BaseAccount { - address: proto.address.parse()?, - pubkey: proto.pub_key.map(PublicKey::try_from).transpose()?, - account_number: proto.account_number, - sequence: proto.sequence, - }) - } -} - -impl From for proto::cosmos::auth::v1beta1::BaseAccount { - fn from(account: BaseAccount) -> proto::cosmos::auth::v1beta1::BaseAccount { - proto::cosmos::auth::v1beta1::BaseAccount { - address: account.address.to_string(), - pub_key: account.pubkey.map(Into::into), - account_number: account.account_number, - sequence: account.sequence, - } - } -} - -/// ModuleAccount defines an account for modules that holds coins on a pool. -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct ModuleAccount { - /// [`BaseAccount`] specification of this module account. - pub base_account: Option, - - /// Name of the module. - pub name: String, - - /// Permissions associated with this module account. - pub permissions: Vec, -} - -impl TryFrom for ModuleAccount { - type Error = ErrorReport; - - fn try_from(proto: proto::cosmos::auth::v1beta1::ModuleAccount) -> Result { - Ok(ModuleAccount { - base_account: proto.base_account.map(TryFrom::try_from).transpose()?, - name: proto.name, - permissions: proto.permissions, - }) - } -} - -impl From for proto::cosmos::auth::v1beta1::ModuleAccount { - fn from(account: ModuleAccount) -> proto::cosmos::auth::v1beta1::ModuleAccount { - proto::cosmos::auth::v1beta1::ModuleAccount { - base_account: account.base_account.map(Into::into), - name: account.name, - permissions: account.permissions, - } - } -} +pub use self::{base_account::BaseAccount, module_account::ModuleAccount}; diff --git a/cosmrs/src/auth/base_account.rs b/cosmrs/src/auth/base_account.rs new file mode 100644 index 00000000..8da6ec23 --- /dev/null +++ b/cosmrs/src/auth/base_account.rs @@ -0,0 +1,52 @@ +use crate::{ + crypto::PublicKey, + proto, + tx::{AccountNumber, SequenceNumber}, + AccountId, ErrorReport, Result, +}; + +/// [`BaseAccount`] defines a base account type. +/// +/// It contains all the necessary fields for basic account functionality. +/// +/// Any custom account type should extend this type for additional functionality +/// (e.g. vesting). +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct BaseAccount { + /// Bech32 [`AccountId`] of this account. + pub address: AccountId, + + /// Optional [`PublicKey`] associated with this account. + pub pubkey: Option, + + /// `account_number` is the account number of the account in state + pub account_number: AccountNumber, + + /// Sequence of the account, which describes the number of committed transactions signed by a + /// given address. + pub sequence: SequenceNumber, +} + +impl TryFrom for BaseAccount { + type Error = ErrorReport; + + fn try_from(proto: proto::cosmos::auth::v1beta1::BaseAccount) -> Result { + Ok(BaseAccount { + address: proto.address.parse()?, + pubkey: proto.pub_key.map(PublicKey::try_from).transpose()?, + account_number: proto.account_number, + sequence: proto.sequence, + }) + } +} + +impl From for proto::cosmos::auth::v1beta1::BaseAccount { + fn from(account: BaseAccount) -> proto::cosmos::auth::v1beta1::BaseAccount { + proto::cosmos::auth::v1beta1::BaseAccount { + address: account.address.to_string(), + pub_key: account.pubkey.map(Into::into), + account_number: account.account_number, + sequence: account.sequence, + } + } +} diff --git a/cosmrs/src/auth/module_account.rs b/cosmrs/src/auth/module_account.rs new file mode 100644 index 00000000..a020662c --- /dev/null +++ b/cosmrs/src/auth/module_account.rs @@ -0,0 +1,37 @@ +use super::BaseAccount; +use crate::{proto, ErrorReport, Result}; + +/// ModuleAccount defines an account for modules that holds coins on a pool. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct ModuleAccount { + /// [`BaseAccount`] specification of this module account. + pub base_account: Option, + + /// Name of the module. + pub name: String, + + /// Permissions associated with this module account. + pub permissions: Vec, +} + +impl TryFrom for ModuleAccount { + type Error = ErrorReport; + + fn try_from(proto: proto::cosmos::auth::v1beta1::ModuleAccount) -> Result { + Ok(ModuleAccount { + base_account: proto.base_account.map(TryFrom::try_from).transpose()?, + name: proto.name, + permissions: proto.permissions, + }) + } +} + +impl From for proto::cosmos::auth::v1beta1::ModuleAccount { + fn from(account: ModuleAccount) -> proto::cosmos::auth::v1beta1::ModuleAccount { + proto::cosmos::auth::v1beta1::ModuleAccount { + base_account: account.base_account.map(Into::into), + name: account.name, + permissions: account.permissions, + } + } +} diff --git a/cosmrs/src/bank.rs b/cosmrs/src/bank.rs index de6c8a59..e663e1a4 100644 --- a/cosmrs/src/bank.rs +++ b/cosmrs/src/bank.rs @@ -2,203 +2,8 @@ //! //! -use crate::{proto, tx::Msg, AccountId, Coin, ErrorReport, Result}; +mod msg_multi_send; +mod msg_send; +mod multi_send_io; -/// MsgSend represents a message to send coins from one account to another. -#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] -pub struct MsgSend { - /// Sender's address. - pub from_address: AccountId, - - /// Recipient's address. - pub to_address: AccountId, - - /// Amount to send - pub amount: Vec, -} - -impl Msg for MsgSend { - type Proto = proto::cosmos::bank::v1beta1::MsgSend; -} - -impl TryFrom for MsgSend { - type Error = ErrorReport; - - fn try_from(proto: proto::cosmos::bank::v1beta1::MsgSend) -> Result { - MsgSend::try_from(&proto) - } -} - -impl TryFrom<&proto::cosmos::bank::v1beta1::MsgSend> for MsgSend { - type Error = ErrorReport; - - fn try_from(proto: &proto::cosmos::bank::v1beta1::MsgSend) -> Result { - Ok(MsgSend { - from_address: proto.from_address.parse()?, - to_address: proto.to_address.parse()?, - amount: proto - .amount - .iter() - .map(TryFrom::try_from) - .collect::>()?, - }) - } -} - -impl From for proto::cosmos::bank::v1beta1::MsgSend { - fn from(coin: MsgSend) -> proto::cosmos::bank::v1beta1::MsgSend { - proto::cosmos::bank::v1beta1::MsgSend::from(&coin) - } -} - -impl From<&MsgSend> for proto::cosmos::bank::v1beta1::MsgSend { - fn from(msg: &MsgSend) -> proto::cosmos::bank::v1beta1::MsgSend { - proto::cosmos::bank::v1beta1::MsgSend { - from_address: msg.from_address.to_string(), - to_address: msg.to_address.to_string(), - amount: msg.amount.iter().map(Into::into).collect(), - } - } -} - -/// MsgMultiSend represents an arbitrary multi-in, multi-out send message. -#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] -pub struct MsgMultiSend { - /// Sender account/amount pairs. - pub inputs: Vec, - - /// Recipient account/amount pairs. - pub outputs: Vec, -} - -impl Msg for MsgMultiSend { - type Proto = proto::cosmos::bank::v1beta1::MsgMultiSend; -} - -impl TryFrom for MsgMultiSend { - type Error = ErrorReport; - - fn try_from(proto: proto::cosmos::bank::v1beta1::MsgMultiSend) -> Result { - MsgMultiSend::try_from(&proto) - } -} - -impl TryFrom<&proto::cosmos::bank::v1beta1::MsgMultiSend> for MsgMultiSend { - type Error = ErrorReport; - - fn try_from(proto: &proto::cosmos::bank::v1beta1::MsgMultiSend) -> Result { - Ok(MsgMultiSend { - inputs: proto - .inputs - .iter() - .map(TryFrom::try_from) - .collect::>()?, - outputs: proto - .outputs - .iter() - .map(TryFrom::try_from) - .collect::>()?, - }) - } -} - -impl From for proto::cosmos::bank::v1beta1::MsgMultiSend { - fn from(coin: MsgMultiSend) -> proto::cosmos::bank::v1beta1::MsgMultiSend { - proto::cosmos::bank::v1beta1::MsgMultiSend::from(&coin) - } -} - -impl From<&MsgMultiSend> for proto::cosmos::bank::v1beta1::MsgMultiSend { - fn from(msg: &MsgMultiSend) -> proto::cosmos::bank::v1beta1::MsgMultiSend { - proto::cosmos::bank::v1beta1::MsgMultiSend { - inputs: msg.inputs.iter().map(Into::into).collect(), - outputs: msg.outputs.iter().map(Into::into).collect(), - } - } -} - -/// Represents a MultiSend Input or Output -#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] -pub struct MultiSendIo { - /// The address that `coins` will be sent to/from - pub address: AccountId, - - /// The coins to send to/from `address` - pub coins: Vec, -} - -impl TryFrom for MultiSendIo { - type Error = ErrorReport; - - fn try_from(proto: proto::cosmos::bank::v1beta1::Input) -> Result { - MultiSendIo::try_from(&proto) - } -} - -impl TryFrom<&proto::cosmos::bank::v1beta1::Input> for MultiSendIo { - type Error = ErrorReport; - - fn try_from(proto: &proto::cosmos::bank::v1beta1::Input) -> Result { - Ok(MultiSendIo { - address: proto.address.parse()?, - coins: proto - .coins - .iter() - .map(TryFrom::try_from) - .collect::>()?, - }) - } -} - -impl TryFrom for MultiSendIo { - type Error = ErrorReport; - - fn try_from(proto: proto::cosmos::bank::v1beta1::Output) -> Result { - MultiSendIo::try_from(&proto) - } -} - -impl TryFrom<&proto::cosmos::bank::v1beta1::Output> for MultiSendIo { - type Error = ErrorReport; - - fn try_from(proto: &proto::cosmos::bank::v1beta1::Output) -> Result { - Ok(MultiSendIo { - address: proto.address.parse()?, - coins: proto - .coins - .iter() - .map(TryFrom::try_from) - .collect::>()?, - }) - } -} - -impl From for proto::cosmos::bank::v1beta1::Output { - fn from(output: MultiSendIo) -> proto::cosmos::bank::v1beta1::Output { - proto::cosmos::bank::v1beta1::Output::from(&output) - } -} - -impl From<&MultiSendIo> for proto::cosmos::bank::v1beta1::Output { - fn from(output: &MultiSendIo) -> proto::cosmos::bank::v1beta1::Output { - proto::cosmos::bank::v1beta1::Output { - address: output.address.to_string(), - coins: output.coins.iter().map(Into::into).collect(), - } - } -} - -impl From for proto::cosmos::bank::v1beta1::Input { - fn from(input: MultiSendIo) -> proto::cosmos::bank::v1beta1::Input { - proto::cosmos::bank::v1beta1::Input::from(&input) - } -} - -impl From<&MultiSendIo> for proto::cosmos::bank::v1beta1::Input { - fn from(input: &MultiSendIo) -> proto::cosmos::bank::v1beta1::Input { - proto::cosmos::bank::v1beta1::Input { - address: input.address.to_string(), - coins: input.coins.iter().map(Into::into).collect(), - } - } -} +pub use self::{msg_multi_send::MsgMultiSend, msg_send::MsgSend, multi_send_io::MultiSendIo}; diff --git a/cosmrs/src/bank/msg_multi_send.rs b/cosmrs/src/bank/msg_multi_send.rs new file mode 100644 index 00000000..b3b404ad --- /dev/null +++ b/cosmrs/src/bank/msg_multi_send.rs @@ -0,0 +1,58 @@ +use super::MultiSendIo; +use crate::{proto, tx::Msg, ErrorReport, Result}; + +/// MsgMultiSend represents an arbitrary multi-in, multi-out send message. +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +pub struct MsgMultiSend { + /// Sender account/amount pairs. + pub inputs: Vec, + + /// Recipient account/amount pairs. + pub outputs: Vec, +} + +impl Msg for MsgMultiSend { + type Proto = proto::cosmos::bank::v1beta1::MsgMultiSend; +} + +impl TryFrom for MsgMultiSend { + type Error = ErrorReport; + + fn try_from(proto: proto::cosmos::bank::v1beta1::MsgMultiSend) -> Result { + MsgMultiSend::try_from(&proto) + } +} + +impl TryFrom<&proto::cosmos::bank::v1beta1::MsgMultiSend> for MsgMultiSend { + type Error = ErrorReport; + + fn try_from(proto: &proto::cosmos::bank::v1beta1::MsgMultiSend) -> Result { + Ok(MsgMultiSend { + inputs: proto + .inputs + .iter() + .map(TryFrom::try_from) + .collect::>()?, + outputs: proto + .outputs + .iter() + .map(TryFrom::try_from) + .collect::>()?, + }) + } +} + +impl From for proto::cosmos::bank::v1beta1::MsgMultiSend { + fn from(coin: MsgMultiSend) -> proto::cosmos::bank::v1beta1::MsgMultiSend { + proto::cosmos::bank::v1beta1::MsgMultiSend::from(&coin) + } +} + +impl From<&MsgMultiSend> for proto::cosmos::bank::v1beta1::MsgMultiSend { + fn from(msg: &MsgMultiSend) -> proto::cosmos::bank::v1beta1::MsgMultiSend { + proto::cosmos::bank::v1beta1::MsgMultiSend { + inputs: msg.inputs.iter().map(Into::into).collect(), + outputs: msg.outputs.iter().map(Into::into).collect(), + } + } +} diff --git a/cosmrs/src/bank/msg_send.rs b/cosmrs/src/bank/msg_send.rs new file mode 100644 index 00000000..2b005f47 --- /dev/null +++ b/cosmrs/src/bank/msg_send.rs @@ -0,0 +1,58 @@ +use crate::{proto, tx::Msg, AccountId, Coin, ErrorReport, Result}; + +/// MsgSend represents a message to send coins from one account to another. +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +pub struct MsgSend { + /// Sender's address. + pub from_address: AccountId, + + /// Recipient's address. + pub to_address: AccountId, + + /// Amount to send + pub amount: Vec, +} + +impl Msg for MsgSend { + type Proto = proto::cosmos::bank::v1beta1::MsgSend; +} + +impl TryFrom for MsgSend { + type Error = ErrorReport; + + fn try_from(proto: proto::cosmos::bank::v1beta1::MsgSend) -> Result { + MsgSend::try_from(&proto) + } +} + +impl TryFrom<&proto::cosmos::bank::v1beta1::MsgSend> for MsgSend { + type Error = ErrorReport; + + fn try_from(proto: &proto::cosmos::bank::v1beta1::MsgSend) -> Result { + Ok(MsgSend { + from_address: proto.from_address.parse()?, + to_address: proto.to_address.parse()?, + amount: proto + .amount + .iter() + .map(TryFrom::try_from) + .collect::>()?, + }) + } +} + +impl From for proto::cosmos::bank::v1beta1::MsgSend { + fn from(coin: MsgSend) -> proto::cosmos::bank::v1beta1::MsgSend { + proto::cosmos::bank::v1beta1::MsgSend::from(&coin) + } +} + +impl From<&MsgSend> for proto::cosmos::bank::v1beta1::MsgSend { + fn from(msg: &MsgSend) -> proto::cosmos::bank::v1beta1::MsgSend { + proto::cosmos::bank::v1beta1::MsgSend { + from_address: msg.from_address.to_string(), + to_address: msg.to_address.to_string(), + amount: msg.amount.iter().map(Into::into).collect(), + } + } +} diff --git a/cosmrs/src/bank/multi_send_io.rs b/cosmrs/src/bank/multi_send_io.rs new file mode 100644 index 00000000..c2d5da01 --- /dev/null +++ b/cosmrs/src/bank/multi_send_io.rs @@ -0,0 +1,87 @@ +use crate::{proto, AccountId, Coin, ErrorReport, Result}; + +/// Represents a MultiSend Input or Output +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +pub struct MultiSendIo { + /// The address that `coins` will be sent to/from + pub address: AccountId, + + /// The coins to send to/from `address` + pub coins: Vec, +} + +impl TryFrom for MultiSendIo { + type Error = ErrorReport; + + fn try_from(proto: proto::cosmos::bank::v1beta1::Input) -> Result { + MultiSendIo::try_from(&proto) + } +} + +impl TryFrom<&proto::cosmos::bank::v1beta1::Input> for MultiSendIo { + type Error = ErrorReport; + + fn try_from(proto: &proto::cosmos::bank::v1beta1::Input) -> Result { + Ok(MultiSendIo { + address: proto.address.parse()?, + coins: proto + .coins + .iter() + .map(TryFrom::try_from) + .collect::>()?, + }) + } +} + +impl TryFrom for MultiSendIo { + type Error = ErrorReport; + + fn try_from(proto: proto::cosmos::bank::v1beta1::Output) -> Result { + MultiSendIo::try_from(&proto) + } +} + +impl TryFrom<&proto::cosmos::bank::v1beta1::Output> for MultiSendIo { + type Error = ErrorReport; + + fn try_from(proto: &proto::cosmos::bank::v1beta1::Output) -> Result { + Ok(MultiSendIo { + address: proto.address.parse()?, + coins: proto + .coins + .iter() + .map(TryFrom::try_from) + .collect::>()?, + }) + } +} + +impl From for proto::cosmos::bank::v1beta1::Output { + fn from(output: MultiSendIo) -> proto::cosmos::bank::v1beta1::Output { + proto::cosmos::bank::v1beta1::Output::from(&output) + } +} + +impl From<&MultiSendIo> for proto::cosmos::bank::v1beta1::Output { + fn from(output: &MultiSendIo) -> proto::cosmos::bank::v1beta1::Output { + proto::cosmos::bank::v1beta1::Output { + address: output.address.to_string(), + coins: output.coins.iter().map(Into::into).collect(), + } + } +} + +impl From for proto::cosmos::bank::v1beta1::Input { + fn from(input: MultiSendIo) -> proto::cosmos::bank::v1beta1::Input { + proto::cosmos::bank::v1beta1::Input::from(&input) + } +} + +impl From<&MultiSendIo> for proto::cosmos::bank::v1beta1::Input { + fn from(input: &MultiSendIo) -> proto::cosmos::bank::v1beta1::Input { + proto::cosmos::bank::v1beta1::Input { + address: input.address.to_string(), + coins: input.coins.iter().map(Into::into).collect(), + } + } +} diff --git a/cosmrs/src/base.rs b/cosmrs/src/base.rs index db0e5524..7602c3c8 100644 --- a/cosmrs/src/base.rs +++ b/cosmrs/src/base.rs @@ -1,288 +1,13 @@ //! Base functionality. -use crate::{proto, Error, ErrorReport, Result}; -use eyre::WrapErr; -use serde::{de, de::Error as _, ser, Deserialize, Serialize}; -use std::{fmt, str::FromStr}; -use subtle_encoding::bech32; +mod account_id; +mod coin; +mod denom; -/// Maximum allowed length (in bytes) for an address. -pub const MAX_ADDRESS_LENGTH: usize = 255; - -/// Account identifiers -#[derive(Clone, Eq, PartialEq, PartialOrd, Ord)] -pub struct AccountId { - /// Account ID encoded as Bech32 - bech32: String, - - /// Length of the human-readable prefix of the address - hrp_length: usize, -} - -impl AccountId { - /// Create an [`AccountId`] with the given human-readable prefix and - /// public key hash. - pub fn new(prefix: &str, bytes: &[u8]) -> Result { - let id = bech32::encode(prefix, &bytes); - - if !prefix.chars().all(|c| matches!(c, 'a'..='z' | '0'..='9')) { - return Err(Error::AccountId { id }) - .wrap_err("expected prefix to be lowercase alphanumeric characters only"); - } - - if matches!(bytes.len(), 1..=MAX_ADDRESS_LENGTH) { - Ok(Self { - bech32: id, - hrp_length: prefix.len(), - }) - } else { - Err(Error::AccountId { id }).wrap_err_with(|| { - format!( - "account ID should be at most {} bytes long, but was {} bytes long", - MAX_ADDRESS_LENGTH, - bytes.len() - ) - }) - } - } - - /// Get the human-readable prefix of this account. - pub fn prefix(&self) -> &str { - &self.bech32[..self.hrp_length] - } - - /// Decode an account ID from Bech32 to an inner byte value. - pub fn to_bytes(&self) -> Vec { - bech32::decode(&self.bech32) - .expect("malformed Bech32 AccountId") - .1 - } -} - -impl AsRef for AccountId { - fn as_ref(&self) -> &str { - &self.bech32 - } -} - -impl fmt::Debug for AccountId { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("AccountId").field(&self.as_ref()).finish() - } -} - -impl fmt::Display for AccountId { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(self.as_ref()) - } -} - -impl FromStr for AccountId { - type Err = ErrorReport; - - fn from_str(s: &str) -> Result { - let (hrp, bytes) = bech32::decode(s).wrap_err(format!("invalid bech32: '{}'", s))?; - Self::new(&hrp, &bytes) - } -} - -impl From for String { - fn from(id: AccountId) -> Self { - id.bech32 - } -} - -impl TryFrom for tendermint::account::Id { - type Error = ErrorReport; - - fn try_from(id: AccountId) -> Result { - tendermint::account::Id::try_from(&id) - } -} - -// TODO(tarcieri): non-fixed-width account ID type -impl TryFrom<&AccountId> for tendermint::account::Id { - type Error = ErrorReport; - - fn try_from(id: &AccountId) -> Result { - let bytes = id.to_bytes(); - let len = bytes.len(); - - match bytes.try_into() { - Ok(bytes) => Ok(tendermint::account::Id::new(bytes)), - _ => Err(Error::AccountId { - id: id.bech32.clone(), - }) - .wrap_err_with(|| format!("invalid length for account ID: {}", len)), - } - } -} - -impl<'de> Deserialize<'de> for AccountId { - fn deserialize>(deserializer: D) -> Result { - String::deserialize(deserializer)? - .parse() - .map_err(D::Error::custom) - } -} - -impl Serialize for AccountId { - fn serialize(&self, serializer: S) -> Result { - self.bech32.serialize(serializer) - } -} +pub use self::{account_id::AccountId, coin::Coin, denom::Denom}; /// Amounts. pub type Amount = u128; -/// Coin defines a token with a denomination and an amount. -#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] -pub struct Coin { - /// Denomination - pub denom: Denom, - - /// Amount - pub amount: Amount, -} - -impl Coin { - /// Constructor - pub fn new(amount: Amount, denom: &str) -> Result { - Ok(Coin { - amount, - denom: denom.parse()?, - }) - } -} - -impl TryFrom for Coin { - type Error = ErrorReport; - - fn try_from(proto: proto::cosmos::base::v1beta1::Coin) -> Result { - Coin::try_from(&proto) - } -} - -impl TryFrom<&proto::cosmos::base::v1beta1::Coin> for Coin { - type Error = ErrorReport; - - fn try_from(proto: &proto::cosmos::base::v1beta1::Coin) -> Result { - Ok(Coin { - denom: proto.denom.parse()?, - amount: proto.amount.parse()?, - }) - } -} - -impl From for proto::cosmos::base::v1beta1::Coin { - fn from(coin: Coin) -> proto::cosmos::base::v1beta1::Coin { - proto::cosmos::base::v1beta1::Coin::from(&coin) - } -} - -impl From<&Coin> for proto::cosmos::base::v1beta1::Coin { - fn from(coin: &Coin) -> proto::cosmos::base::v1beta1::Coin { - proto::cosmos::base::v1beta1::Coin { - denom: coin.denom.to_string(), - amount: coin.amount.to_string(), - } - } -} - -impl fmt::Display for Coin { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // See: https://github.com/cosmos/cosmos-sdk/blob/v0.42.4/types/coin.go#L643-L645 - write!(f, "{}{}", self.amount, self.denom) - } -} - -/// Denomination. -#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] -pub struct Denom(String); - -impl AsRef for Denom { - fn as_ref(&self) -> &str { - self.0.as_ref() - } -} - -impl fmt::Display for Denom { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(self.as_ref()) - } -} - -impl FromStr for Denom { - type Err = ErrorReport; - - fn from_str(s: &str) -> Result { - // TODO(tarcieri): ensure this is the proper validation for a denom name - if s.chars() - .all(|c| matches!(c, 'A'..='Z' | 'a'..='z' | '0'..='9' | '/')) - { - Ok(Denom(s.to_owned())) - } else { - Err(Error::Denom { name: s.to_owned() }.into()) - } - } -} - -impl<'de> Deserialize<'de> for Denom { - fn deserialize>(deserializer: D) -> Result { - String::deserialize(deserializer)? - .parse() - .map_err(D::Error::custom) - } -} - -impl Serialize for Denom { - fn serialize(&self, serializer: S) -> Result { - self.0.serialize(serializer) - } -} - /// Gas cost. pub type Gas = u64; - -#[cfg(test)] -mod tests { - use super::{AccountId, Coin, Denom}; - - #[test] - fn account_id() { - "juno1cma4czt2jnydvrvz3lrc9jvcmhpjxtds95s3c6" - .parse::() - .unwrap(); - } - - #[test] - fn account_id_with_digit() { - "okp41urdh3smlstyafjtyg0d606egllhwp8kvnw0d2f" - .parse::() - .unwrap(); - } - - #[test] - fn account_id_into_string() { - let account_id = "juno10j9gpw9t4jsz47qgnkvl5n3zlm2fz72k67rxsg" - .parse::() - .unwrap(); - - let s: String = account_id.into(); - assert_eq!(s, "juno10j9gpw9t4jsz47qgnkvl5n3zlm2fz72k67rxsg".to_string()); - } - - #[test] - fn denom_from_str() { - assert!( - "ibc/9F53D255F5320A4BE124FF20C29D46406E126CE8A09B00CA8D3CFF7905119728" - .parse::() - .is_ok() - ); - } - - #[test] - fn coin() { - Coin::new(1000, "uatom").unwrap(); - } -} diff --git a/cosmrs/src/base/account_id.rs b/cosmrs/src/base/account_id.rs new file mode 100644 index 00000000..09218d4d --- /dev/null +++ b/cosmrs/src/base/account_id.rs @@ -0,0 +1,160 @@ +use crate::{Error, ErrorReport, Result}; +use eyre::WrapErr; +use serde::{de, de::Error as _, ser, Deserialize, Serialize}; +use std::{fmt, str::FromStr}; +use subtle_encoding::bech32; + +/// Account identifiers +#[derive(Clone, Eq, PartialEq, PartialOrd, Ord)] +pub struct AccountId { + /// Account ID encoded as Bech32 + bech32: String, + + /// Length of the human-readable prefix of the address + hrp_length: usize, +} + +impl AccountId { + /// Maximum allowed length (in bytes) of an address. + pub const MAX_LENGTH: usize = 255; + + /// Create an [`AccountId`] with the given human-readable prefix and + /// public key hash. + pub fn new(prefix: &str, bytes: &[u8]) -> Result { + let id = bech32::encode(prefix, &bytes); + + if !prefix.chars().all(|c| matches!(c, 'a'..='z' | '0'..='9')) { + return Err(Error::AccountId { id }) + .wrap_err("expected prefix to be lowercase alphanumeric characters only"); + } + + if matches!(bytes.len(), 1..=Self::MAX_LENGTH) { + Ok(Self { + bech32: id, + hrp_length: prefix.len(), + }) + } else { + Err(Error::AccountId { id }).wrap_err_with(|| { + format!( + "account ID should be at most {} bytes long, but was {} bytes long", + Self::MAX_LENGTH, + bytes.len() + ) + }) + } + } + + /// Get the human-readable prefix of this account. + pub fn prefix(&self) -> &str { + &self.bech32[..self.hrp_length] + } + + /// Decode an account ID from Bech32 to an inner byte value. + pub fn to_bytes(&self) -> Vec { + bech32::decode(&self.bech32) + .expect("malformed Bech32 AccountId") + .1 + } +} + +impl AsRef for AccountId { + fn as_ref(&self) -> &str { + &self.bech32 + } +} + +impl fmt::Debug for AccountId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("AccountId").field(&self.as_ref()).finish() + } +} + +impl fmt::Display for AccountId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.as_ref()) + } +} + +impl FromStr for AccountId { + type Err = ErrorReport; + + fn from_str(s: &str) -> Result { + let (hrp, bytes) = bech32::decode(s).wrap_err(format!("invalid bech32: '{}'", s))?; + Self::new(&hrp, &bytes) + } +} + +impl From for String { + fn from(id: AccountId) -> Self { + id.bech32 + } +} + +impl TryFrom for tendermint::account::Id { + type Error = ErrorReport; + + fn try_from(id: AccountId) -> Result { + tendermint::account::Id::try_from(&id) + } +} + +// TODO(tarcieri): non-fixed-width account ID type +impl TryFrom<&AccountId> for tendermint::account::Id { + type Error = ErrorReport; + + fn try_from(id: &AccountId) -> Result { + let bytes = id.to_bytes(); + let len = bytes.len(); + + match bytes.try_into() { + Ok(bytes) => Ok(tendermint::account::Id::new(bytes)), + _ => Err(Error::AccountId { + id: id.bech32.clone(), + }) + .wrap_err_with(|| format!("invalid length for account ID: {}", len)), + } + } +} + +impl<'de> Deserialize<'de> for AccountId { + fn deserialize>(deserializer: D) -> Result { + String::deserialize(deserializer)? + .parse() + .map_err(D::Error::custom) + } +} + +impl Serialize for AccountId { + fn serialize(&self, serializer: S) -> Result { + self.bech32.serialize(serializer) + } +} + +#[cfg(test)] +mod tests { + use super::AccountId; + + #[test] + fn parse() { + "juno1cma4czt2jnydvrvz3lrc9jvcmhpjxtds95s3c6" + .parse::() + .unwrap(); + } + + #[test] + fn with_digit() { + "okp41urdh3smlstyafjtyg0d606egllhwp8kvnw0d2f" + .parse::() + .unwrap(); + } + + #[test] + fn to_string() { + let account_id = "juno10j9gpw9t4jsz47qgnkvl5n3zlm2fz72k67rxsg" + .parse::() + .unwrap(); + + let s: String = account_id.into(); + assert_eq!(s, "juno10j9gpw9t4jsz47qgnkvl5n3zlm2fz72k67rxsg".to_string()); + } +} diff --git a/cosmrs/src/base/coin.rs b/cosmrs/src/base/coin.rs new file mode 100644 index 00000000..fe6e6e52 --- /dev/null +++ b/cosmrs/src/base/coin.rs @@ -0,0 +1,75 @@ +use super::{Amount, Denom}; +use crate::{proto, ErrorReport, Result}; +use serde::{Deserialize, Serialize}; +use std::fmt; + +/// Coin defines a token with a denomination and an amount. +#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +pub struct Coin { + /// Denomination + pub denom: Denom, + + /// Amount + pub amount: Amount, +} + +impl Coin { + /// Constructor + pub fn new(amount: Amount, denom: &str) -> Result { + Ok(Coin { + amount, + denom: denom.parse()?, + }) + } +} + +impl TryFrom for Coin { + type Error = ErrorReport; + + fn try_from(proto: proto::cosmos::base::v1beta1::Coin) -> Result { + Coin::try_from(&proto) + } +} + +impl TryFrom<&proto::cosmos::base::v1beta1::Coin> for Coin { + type Error = ErrorReport; + + fn try_from(proto: &proto::cosmos::base::v1beta1::Coin) -> Result { + Ok(Coin { + denom: proto.denom.parse()?, + amount: proto.amount.parse()?, + }) + } +} + +impl From for proto::cosmos::base::v1beta1::Coin { + fn from(coin: Coin) -> proto::cosmos::base::v1beta1::Coin { + proto::cosmos::base::v1beta1::Coin::from(&coin) + } +} + +impl From<&Coin> for proto::cosmos::base::v1beta1::Coin { + fn from(coin: &Coin) -> proto::cosmos::base::v1beta1::Coin { + proto::cosmos::base::v1beta1::Coin { + denom: coin.denom.to_string(), + amount: coin.amount.to_string(), + } + } +} + +impl fmt::Display for Coin { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // See: https://github.com/cosmos/cosmos-sdk/blob/v0.42.4/types/coin.go#L643-L645 + write!(f, "{}{}", self.amount, self.denom) + } +} + +#[cfg(test)] +mod tests { + use super::Coin; + + #[test] + fn new() { + Coin::new(1000, "uatom").unwrap(); + } +} diff --git a/cosmrs/src/base/denom.rs b/cosmrs/src/base/denom.rs new file mode 100644 index 00000000..406ce8ec --- /dev/null +++ b/cosmrs/src/base/denom.rs @@ -0,0 +1,62 @@ +use crate::{Error, ErrorReport, Result}; +use serde::{de, de::Error as _, ser, Deserialize, Serialize}; +use std::{fmt, str::FromStr}; + +/// Denomination. +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +pub struct Denom(String); + +impl AsRef for Denom { + fn as_ref(&self) -> &str { + self.0.as_ref() + } +} + +impl fmt::Display for Denom { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.as_ref()) + } +} + +impl FromStr for Denom { + type Err = ErrorReport; + + fn from_str(s: &str) -> Result { + // TODO(tarcieri): ensure this is the proper validation for a denom name + if s.chars() + .all(|c| matches!(c, 'A'..='Z' | 'a'..='z' | '0'..='9' | '/')) + { + Ok(Denom(s.to_owned())) + } else { + Err(Error::Denom { name: s.to_owned() }.into()) + } + } +} + +impl<'de> Deserialize<'de> for Denom { + fn deserialize>(deserializer: D) -> Result { + String::deserialize(deserializer)? + .parse() + .map_err(D::Error::custom) + } +} + +impl Serialize for Denom { + fn serialize(&self, serializer: S) -> Result { + self.0.serialize(serializer) + } +} + +#[cfg(test)] +mod tests { + use super::Denom; + + #[test] + fn parse() { + assert!( + "ibc/9F53D255F5320A4BE124FF20C29D46406E126CE8A09B00CA8D3CFF7905119728" + .parse::() + .is_ok() + ); + } +} diff --git a/cosmrs/src/cosmwasm.rs b/cosmrs/src/cosmwasm.rs index ed0e924f..046b76a8 100644 --- a/cosmrs/src/cosmwasm.rs +++ b/cosmrs/src/cosmwasm.rs @@ -3,731 +3,31 @@ //! - Tutorial: //! - Protocol Docs: -pub use crate::proto::cosmwasm::wasm::v1::AccessType; -use crate::{ - proto::{self, traits::ParseOptional}, - tx::Msg, - AccountId, Coin, Error, ErrorReport, Result, +mod absolute_tx_position; +mod access_config; +mod code_info_response; +mod contract_code_history_entry; +mod contract_info; +mod msg_clear_admin; +mod msg_execute_contract; +mod msg_initiate_contract; +mod msg_migrate_contract; +mod msg_store_code; +mod msg_update_admin; +mod query_code_response; + +pub use self::{ + absolute_tx_position::AbsoluteTxPosition, + access_config::AccessConfig, + code_info_response::CodeInfoResponse, + contract_code_history_entry::ContractCodeHistoryEntry, + contract_info::ContractInfo, + msg_execute_contract::{MsgExecuteContract, MsgExecuteContractResponse}, + msg_initiate_contract::{MsgInstantiateContract, MsgInstantiateContractResponse}, + msg_store_code::{MsgStoreCode, MsgStoreCodeResponse}, + query_code_response::QueryCodeResponse, }; -use cosmos_sdk_proto::cosmwasm::wasm::v1::ContractCodeHistoryOperationType; +pub use crate::proto::cosmwasm::wasm::v1::AccessType; /// The ID of a particular contract code assigned by the chain. pub type ContractCodeId = u64; - -/// AccessConfig access control type. -#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] -pub struct AccessConfig { - /// Access type granted. - pub permission: AccessType, - - /// Account address with the associated permission. - pub address: AccountId, -} - -impl TryFrom for AccessConfig { - type Error = ErrorReport; - - fn try_from(proto: proto::cosmwasm::wasm::v1::AccessConfig) -> Result { - AccessConfig::try_from(&proto) - } -} - -impl TryFrom<&proto::cosmwasm::wasm::v1::AccessConfig> for AccessConfig { - type Error = ErrorReport; - - fn try_from(proto: &proto::cosmwasm::wasm::v1::AccessConfig) -> Result { - Ok(AccessConfig { - permission: AccessType::from_i32(proto.permission).ok_or(Error::InvalidEnumValue { - name: "permission", - found_value: proto.permission, - })?, - address: proto.address.parse()?, - }) - } -} - -impl From for proto::cosmwasm::wasm::v1::AccessConfig { - fn from(config: AccessConfig) -> proto::cosmwasm::wasm::v1::AccessConfig { - proto::cosmwasm::wasm::v1::AccessConfig::from(&config) - } -} - -impl From<&AccessConfig> for proto::cosmwasm::wasm::v1::AccessConfig { - fn from(config: &AccessConfig) -> proto::cosmwasm::wasm::v1::AccessConfig { - proto::cosmwasm::wasm::v1::AccessConfig { - permission: config.permission as i32, - address: config.address.to_string(), - } - } -} - -/// MsgStoreCode submit Wasm code to the system -#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] -pub struct MsgStoreCode { - /// Sender is the that actor that signed the messages - pub sender: AccountId, - - /// WASMByteCode can be raw or gzip compressed - pub wasm_byte_code: Vec, - - /// InstantiatePermission access control to apply on contract creation, - /// optional - pub instantiate_permission: Option, -} - -impl Msg for MsgStoreCode { - type Proto = proto::cosmwasm::wasm::v1::MsgStoreCode; -} - -impl TryFrom for MsgStoreCode { - type Error = ErrorReport; - - fn try_from(proto: proto::cosmwasm::wasm::v1::MsgStoreCode) -> Result { - Ok(MsgStoreCode { - sender: proto.sender.parse()?, - wasm_byte_code: proto.wasm_byte_code, - instantiate_permission: proto - .instantiate_permission - .map(TryFrom::try_from) - .transpose()?, - }) - } -} - -impl From for proto::cosmwasm::wasm::v1::MsgStoreCode { - fn from(msg: MsgStoreCode) -> proto::cosmwasm::wasm::v1::MsgStoreCode { - proto::cosmwasm::wasm::v1::MsgStoreCode { - sender: msg.sender.to_string(), - wasm_byte_code: msg.wasm_byte_code, - instantiate_permission: msg.instantiate_permission.map(Into::into), - } - } -} - -/// MsgStoreCodeResponse returns store result data. -#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)] -pub struct MsgStoreCodeResponse { - /// CodeID is the reference to the stored WASM code - pub code_id: u64, -} - -impl Msg for MsgStoreCodeResponse { - type Proto = proto::cosmwasm::wasm::v1::MsgStoreCodeResponse; -} - -impl TryFrom for MsgStoreCodeResponse { - type Error = ErrorReport; - - fn try_from( - proto: proto::cosmwasm::wasm::v1::MsgStoreCodeResponse, - ) -> Result { - Ok(MsgStoreCodeResponse { - code_id: proto.code_id, - }) - } -} - -impl From for proto::cosmwasm::wasm::v1::MsgStoreCodeResponse { - fn from(msg: MsgStoreCodeResponse) -> proto::cosmwasm::wasm::v1::MsgStoreCodeResponse { - proto::cosmwasm::wasm::v1::MsgStoreCodeResponse { - code_id: msg.code_id, - } - } -} - -/// MsgInstantiateContract create a new smart contract instance for the given -/// code id. -#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] -pub struct MsgInstantiateContract { - /// Sender is the that actor that signed the messages - pub sender: AccountId, - - /// Admin is an optional address that can execute migrations - pub admin: Option, - - /// CodeID is the reference to the stored WASM code - pub code_id: u64, - - /// Label is optional metadata to be stored with a contract instance. - pub label: Option, - - /// Msg json encoded message to be passed to the contract on instantiation - pub msg: Vec, - - /// Funds coins that are transferred to the contract on instantiation - pub funds: Vec, -} - -impl Msg for MsgInstantiateContract { - type Proto = proto::cosmwasm::wasm::v1::MsgInstantiateContract; -} - -impl TryFrom for MsgInstantiateContract { - type Error = ErrorReport; - - fn try_from( - proto: proto::cosmwasm::wasm::v1::MsgInstantiateContract, - ) -> Result { - Ok(MsgInstantiateContract { - sender: proto.sender.parse()?, - admin: proto.admin.parse_optional()?, - code_id: proto.code_id, - label: proto.label.parse_optional()?, - msg: proto.msg, - funds: proto - .funds - .into_iter() - .map(TryFrom::try_from) - .collect::>()?, - }) - } -} - -impl From for proto::cosmwasm::wasm::v1::MsgInstantiateContract { - fn from(msg: MsgInstantiateContract) -> proto::cosmwasm::wasm::v1::MsgInstantiateContract { - proto::cosmwasm::wasm::v1::MsgInstantiateContract { - sender: msg.sender.to_string(), - admin: msg.admin.map(|admin| admin.to_string()).unwrap_or_default(), - code_id: msg.code_id, - label: msg.label.unwrap_or_default(), - msg: msg.msg, - funds: msg.funds.into_iter().map(Into::into).collect(), - } - } -} - -/// MsgInstantiateContractResponse return instantiation result data -#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] -pub struct MsgInstantiateContractResponse { - /// Address is the bech32 address of the new contract instance. - pub address: AccountId, - - /// Data contains base64-encoded bytes to returned from the contract - pub data: Vec, -} - -impl Msg for MsgInstantiateContractResponse { - type Proto = proto::cosmwasm::wasm::v1::MsgInstantiateContractResponse; -} - -impl TryFrom - for MsgInstantiateContractResponse -{ - type Error = ErrorReport; - - fn try_from( - proto: proto::cosmwasm::wasm::v1::MsgInstantiateContractResponse, - ) -> Result { - Ok(MsgInstantiateContractResponse { - address: proto.address.parse()?, - data: proto.data, - }) - } -} - -impl From - for proto::cosmwasm::wasm::v1::MsgInstantiateContractResponse -{ - fn from( - msg: MsgInstantiateContractResponse, - ) -> proto::cosmwasm::wasm::v1::MsgInstantiateContractResponse { - proto::cosmwasm::wasm::v1::MsgInstantiateContractResponse { - address: msg.address.to_string(), - data: msg.data, - } - } -} - -/// MsgExecuteContract submits the given message data to a smart contract -#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] -pub struct MsgExecuteContract { - /// Sender is the that actor that signed the messages - pub sender: AccountId, - - /// Contract is the address of the smart contract - pub contract: AccountId, - - /// Msg json encoded message to be passed to the contract - pub msg: Vec, - - /// Funds coins that are transferred to the contract on execution - pub funds: Vec, -} - -impl Msg for MsgExecuteContract { - type Proto = proto::cosmwasm::wasm::v1::MsgExecuteContract; -} - -impl TryFrom for MsgExecuteContract { - type Error = ErrorReport; - - fn try_from( - proto: proto::cosmwasm::wasm::v1::MsgExecuteContract, - ) -> Result { - Ok(MsgExecuteContract { - sender: proto.sender.parse()?, - contract: proto.contract.parse()?, - msg: proto.msg.into_iter().map(Into::into).collect(), - funds: proto - .funds - .iter() - .map(TryFrom::try_from) - .collect::>()?, - }) - } -} - -impl From for proto::cosmwasm::wasm::v1::MsgExecuteContract { - fn from(msg: MsgExecuteContract) -> proto::cosmwasm::wasm::v1::MsgExecuteContract { - proto::cosmwasm::wasm::v1::MsgExecuteContract { - sender: msg.sender.to_string(), - contract: msg.contract.to_string(), - msg: msg.msg, - funds: msg.funds.iter().map(Into::into).collect(), - } - } -} - -/// MsgExecuteContractResponse returns execution result data. -#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] -pub struct MsgExecuteContractResponse { - /// Data contains base64-encoded bytes to returned from the contract - pub data: Vec, -} - -impl Msg for MsgExecuteContractResponse { - type Proto = proto::cosmwasm::wasm::v1::MsgExecuteContractResponse; -} - -impl TryFrom for MsgExecuteContractResponse { - type Error = ErrorReport; - - fn try_from( - proto: proto::cosmwasm::wasm::v1::MsgExecuteContractResponse, - ) -> Result { - Ok(MsgExecuteContractResponse { data: proto.data }) - } -} - -impl From for proto::cosmwasm::wasm::v1::MsgExecuteContractResponse { - fn from( - msg: MsgExecuteContractResponse, - ) -> proto::cosmwasm::wasm::v1::MsgExecuteContractResponse { - proto::cosmwasm::wasm::v1::MsgExecuteContractResponse { data: msg.data } - } -} - -/// MsgMigrateContract runs a code upgrade/ downgrade for a smart contract -#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] -pub struct MsgMigrateContract { - /// Sender is the that actor that signed the messages - pub sender: AccountId, - - /// Contract is the address of the smart contract - pub contract: AccountId, - - /// CodeID references the new WASM code - pub code_id: u64, - - /// Msg json encoded message to be passed to the contract on migration - pub msg: Vec, -} - -impl Msg for MsgMigrateContract { - type Proto = proto::cosmwasm::wasm::v1::MsgMigrateContract; -} - -impl TryFrom for MsgMigrateContract { - type Error = ErrorReport; - - fn try_from( - proto: proto::cosmwasm::wasm::v1::MsgMigrateContract, - ) -> Result { - Ok(MsgMigrateContract { - sender: proto.sender.parse()?, - contract: proto.contract.parse()?, - code_id: proto.code_id, - msg: proto.msg, - }) - } -} - -impl From for proto::cosmwasm::wasm::v1::MsgMigrateContract { - fn from(msg: MsgMigrateContract) -> proto::cosmwasm::wasm::v1::MsgMigrateContract { - proto::cosmwasm::wasm::v1::MsgMigrateContract { - sender: msg.sender.to_string(), - contract: msg.contract.to_string(), - code_id: msg.code_id, - msg: msg.msg, - } - } -} - -/// MsgMigrateContractResponse returns contract migration result data. -#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] -pub struct MsgMigrateContractResponse { - /// Data contains same raw bytes returned as data from the wasm contract. - /// (May be empty) - pub data: Vec, -} - -impl Msg for MsgMigrateContractResponse { - type Proto = proto::cosmwasm::wasm::v1::MsgMigrateContractResponse; -} - -impl TryFrom for MsgMigrateContractResponse { - type Error = ErrorReport; - - fn try_from( - proto: proto::cosmwasm::wasm::v1::MsgMigrateContractResponse, - ) -> Result { - Ok(MsgMigrateContractResponse { data: proto.data }) - } -} - -impl From for proto::cosmwasm::wasm::v1::MsgMigrateContractResponse { - fn from( - msg: MsgMigrateContractResponse, - ) -> proto::cosmwasm::wasm::v1::MsgMigrateContractResponse { - proto::cosmwasm::wasm::v1::MsgMigrateContractResponse { data: msg.data } - } -} - -/// MsgUpdateAdmin sets a new admin for a smart contract -#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] -pub struct MsgUpdateAdmin { - /// Sender is the that actor that signed the messages - pub sender: AccountId, - - /// NewAdmin address to be set - pub new_admin: AccountId, - - /// Contract is the address of the smart contract - pub contract: AccountId, -} - -impl Msg for MsgUpdateAdmin { - type Proto = proto::cosmwasm::wasm::v1::MsgUpdateAdmin; -} - -impl TryFrom for MsgUpdateAdmin { - type Error = ErrorReport; - - fn try_from(proto: proto::cosmwasm::wasm::v1::MsgUpdateAdmin) -> Result { - MsgUpdateAdmin::try_from(&proto) - } -} - -impl TryFrom<&proto::cosmwasm::wasm::v1::MsgUpdateAdmin> for MsgUpdateAdmin { - type Error = ErrorReport; - - fn try_from(proto: &proto::cosmwasm::wasm::v1::MsgUpdateAdmin) -> Result { - Ok(MsgUpdateAdmin { - sender: proto.sender.parse()?, - new_admin: proto.new_admin.parse()?, - contract: proto.contract.parse()?, - }) - } -} - -impl From for proto::cosmwasm::wasm::v1::MsgUpdateAdmin { - fn from(msg: MsgUpdateAdmin) -> proto::cosmwasm::wasm::v1::MsgUpdateAdmin { - proto::cosmwasm::wasm::v1::MsgUpdateAdmin::from(&msg) - } -} - -impl From<&MsgUpdateAdmin> for proto::cosmwasm::wasm::v1::MsgUpdateAdmin { - fn from(msg: &MsgUpdateAdmin) -> proto::cosmwasm::wasm::v1::MsgUpdateAdmin { - proto::cosmwasm::wasm::v1::MsgUpdateAdmin { - sender: msg.sender.to_string(), - new_admin: msg.new_admin.to_string(), - contract: msg.contract.to_string(), - } - } -} - -/// MsgUpdateAdminResponse returns empty data -#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)] -pub struct MsgUpdateAdminResponse {} - -impl Msg for MsgUpdateAdminResponse { - type Proto = proto::cosmwasm::wasm::v1::MsgUpdateAdminResponse; -} - -impl TryFrom for MsgUpdateAdminResponse { - type Error = ErrorReport; - - fn try_from( - _proto: proto::cosmwasm::wasm::v1::MsgUpdateAdminResponse, - ) -> Result { - Ok(MsgUpdateAdminResponse {}) - } -} - -impl From for proto::cosmwasm::wasm::v1::MsgUpdateAdminResponse { - fn from(_msg: MsgUpdateAdminResponse) -> proto::cosmwasm::wasm::v1::MsgUpdateAdminResponse { - proto::cosmwasm::wasm::v1::MsgUpdateAdminResponse {} - } -} - -/// MsgClearAdmin removes any admin stored for a smart contract -#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] -pub struct MsgClearAdmin { - /// Sender is the that actor that signed the messages - pub sender: AccountId, - - /// Contract is the address of the smart contract - pub contract: AccountId, -} - -impl Msg for MsgClearAdmin { - type Proto = proto::cosmwasm::wasm::v1::MsgClearAdmin; -} - -impl TryFrom for MsgClearAdmin { - type Error = ErrorReport; - - fn try_from(proto: proto::cosmwasm::wasm::v1::MsgClearAdmin) -> Result { - MsgClearAdmin::try_from(&proto) - } -} - -impl TryFrom<&proto::cosmwasm::wasm::v1::MsgClearAdmin> for MsgClearAdmin { - type Error = ErrorReport; - - fn try_from(proto: &proto::cosmwasm::wasm::v1::MsgClearAdmin) -> Result { - Ok(MsgClearAdmin { - sender: proto.sender.parse()?, - contract: proto.contract.parse()?, - }) - } -} - -impl From for proto::cosmwasm::wasm::v1::MsgClearAdmin { - fn from(msg: MsgClearAdmin) -> proto::cosmwasm::wasm::v1::MsgClearAdmin { - proto::cosmwasm::wasm::v1::MsgClearAdmin::from(&msg) - } -} - -impl From<&MsgClearAdmin> for proto::cosmwasm::wasm::v1::MsgClearAdmin { - fn from(msg: &MsgClearAdmin) -> proto::cosmwasm::wasm::v1::MsgClearAdmin { - proto::cosmwasm::wasm::v1::MsgClearAdmin { - sender: msg.sender.to_string(), - contract: msg.contract.to_string(), - } - } -} - -/// MsgClearAdminResponse returns empty data -#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)] -pub struct MsgClearAdminResponse {} - -impl Msg for MsgClearAdminResponse { - type Proto = proto::cosmwasm::wasm::v1::MsgClearAdminResponse; -} - -impl TryFrom for MsgClearAdminResponse { - type Error = ErrorReport; - - fn try_from( - _proto: proto::cosmwasm::wasm::v1::MsgClearAdminResponse, - ) -> Result { - Ok(MsgClearAdminResponse {}) - } -} - -impl From for proto::cosmwasm::wasm::v1::MsgClearAdminResponse { - fn from(_msg: MsgClearAdminResponse) -> proto::cosmwasm::wasm::v1::MsgClearAdminResponse { - proto::cosmwasm::wasm::v1::MsgClearAdminResponse {} - } -} - -/// CodeInfoResponse contains code meta data from CodeInfo -#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] -pub struct CodeInfoResponse { - /// CodeId of the stored contract code. - pub code_id: ContractCodeId, - - /// Bech32 [`AccountId`] of the creator of this smart contract. - pub creator: AccountId, - - /// sha256 hash of the code stored - pub data_hash: Vec, -} - -impl TryFrom for CodeInfoResponse { - type Error = ErrorReport; - - fn try_from(proto: proto::cosmwasm::wasm::v1::CodeInfoResponse) -> Result { - Ok(CodeInfoResponse { - code_id: proto.code_id, - creator: proto.creator.parse()?, - data_hash: proto.data_hash, - }) - } -} - -impl From for proto::cosmwasm::wasm::v1::CodeInfoResponse { - fn from(code_info: CodeInfoResponse) -> Self { - proto::cosmwasm::wasm::v1::CodeInfoResponse { - code_id: code_info.code_id, - creator: code_info.creator.to_string(), - data_hash: code_info.data_hash, - } - } -} - -/// QueryCodeResponse is the response type for the Query/Code RPC method. -#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] -pub struct QueryCodeResponse { - /// If available, the associated code ID metadata. - pub code_info: Option, - - /// The original wasm bytes. - pub data: Vec, -} - -impl TryFrom for QueryCodeResponse { - type Error = ErrorReport; - - fn try_from(proto: proto::cosmwasm::wasm::v1::QueryCodeResponse) -> Result { - Ok(QueryCodeResponse { - code_info: proto.code_info.map(TryFrom::try_from).transpose()?, - data: proto.data, - }) - } -} - -impl From for proto::cosmwasm::wasm::v1::QueryCodeResponse { - fn from(response: QueryCodeResponse) -> Self { - proto::cosmwasm::wasm::v1::QueryCodeResponse { - code_info: response.code_info.map(Into::into), - data: response.data, - } - } -} - -/// ContractInfo stores a WASM contract instance -#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] -pub struct ContractInfo { - /// Reference to the stored Wasm code. - pub code_id: ContractCodeId, - - /// Creator address who initially instantiated the contract. - pub creator: AccountId, - - /// Admin is an optional address that can execute migrations. - pub admin: Option, - - /// Label is optional metadata to be stored with a contract instance. - pub label: String, - - /// Created Tx position when the contract was instantiated. - // Note that this data should kept internal and not be exposed via query results. - // Just use for sorting. - pub created: Option, - - /// The IBC port ID assigned to this contract by wasmd. - /// This is set for all IBC contracts (). - pub ibc_port_id: String, -} - -impl TryFrom for ContractInfo { - type Error = ErrorReport; - - fn try_from(proto: proto::cosmwasm::wasm::v1::ContractInfo) -> Result { - Ok(ContractInfo { - code_id: proto.code_id, - creator: proto.creator.parse()?, - admin: proto.admin.parse_optional()?, - label: proto.label, - created: proto.created.map(TryFrom::try_from).transpose()?, - ibc_port_id: proto.ibc_port_id, - }) - } -} - -impl From for proto::cosmwasm::wasm::v1::ContractInfo { - fn from(info: ContractInfo) -> Self { - proto::cosmwasm::wasm::v1::ContractInfo { - code_id: info.code_id, - creator: info.creator.to_string(), - admin: info - .admin - .map(|admin| admin.to_string()) - .unwrap_or_default(), - label: info.label, - created: info.created.map(Into::into), - ibc_port_id: info.ibc_port_id, - extension: None, - } - } -} - -/// ContractCodeHistoryEntry metadata to a contract. -#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] -pub struct ContractCodeHistoryEntry { - /// The source of this history entry. - pub operation: ContractCodeHistoryOperationType, - - /// Reference to the stored Wasm code. - pub code_id: ContractCodeId, - - /// Updated Tx position when the operation was executed. - pub updated: Option, - - /// Raw message returned by a wasm contract. - pub msg: Vec, -} - -impl TryFrom for ContractCodeHistoryEntry { - type Error = ErrorReport; - - fn try_from( - proto: proto::cosmwasm::wasm::v1::ContractCodeHistoryEntry, - ) -> Result { - Ok(ContractCodeHistoryEntry { - operation: ContractCodeHistoryOperationType::from_i32(proto.operation).ok_or( - Error::InvalidEnumValue { - name: "operation", - found_value: proto.operation, - }, - )?, - code_id: proto.code_id, - updated: None, - msg: vec![], - }) - } -} - -/// AbsoluteTxPosition is a unique transaction position that allows for global -/// ordering of transactions. -#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] -pub struct AbsoluteTxPosition { - /// BlockHeight is the block the contract was created at - pub block_height: u64, - /// TxIndex is a monotonic counter within the block (actual transaction index, or gas consumed) - pub tx_index: u64, -} - -impl TryFrom for AbsoluteTxPosition { - type Error = ErrorReport; - - fn try_from( - proto: proto::cosmwasm::wasm::v1::AbsoluteTxPosition, - ) -> Result { - Ok(AbsoluteTxPosition { - block_height: proto.block_height, - tx_index: proto.tx_index, - }) - } -} - -impl From for proto::cosmwasm::wasm::v1::AbsoluteTxPosition { - fn from(pos: AbsoluteTxPosition) -> Self { - proto::cosmwasm::wasm::v1::AbsoluteTxPosition { - block_height: pos.block_height, - tx_index: pos.tx_index, - } - } -} diff --git a/cosmrs/src/cosmwasm/absolute_tx_position.rs b/cosmrs/src/cosmwasm/absolute_tx_position.rs new file mode 100644 index 00000000..d44ff726 --- /dev/null +++ b/cosmrs/src/cosmwasm/absolute_tx_position.rs @@ -0,0 +1,33 @@ +use crate::{proto, ErrorReport, Result}; + +/// AbsoluteTxPosition is a unique transaction position that allows for global +/// ordering of transactions. +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +pub struct AbsoluteTxPosition { + /// BlockHeight is the block the contract was created at + pub block_height: u64, + /// TxIndex is a monotonic counter within the block (actual transaction index, or gas consumed) + pub tx_index: u64, +} + +impl TryFrom for AbsoluteTxPosition { + type Error = ErrorReport; + + fn try_from( + proto: proto::cosmwasm::wasm::v1::AbsoluteTxPosition, + ) -> Result { + Ok(AbsoluteTxPosition { + block_height: proto.block_height, + tx_index: proto.tx_index, + }) + } +} + +impl From for proto::cosmwasm::wasm::v1::AbsoluteTxPosition { + fn from(pos: AbsoluteTxPosition) -> Self { + proto::cosmwasm::wasm::v1::AbsoluteTxPosition { + block_height: pos.block_height, + tx_index: pos.tx_index, + } + } +} diff --git a/cosmrs/src/cosmwasm/access_config.rs b/cosmrs/src/cosmwasm/access_config.rs new file mode 100644 index 00000000..dd3c397b --- /dev/null +++ b/cosmrs/src/cosmwasm/access_config.rs @@ -0,0 +1,49 @@ +use super::AccessType; +use crate::{proto, AccountId, Error, ErrorReport, Result}; + +/// AccessConfig access control type. +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +pub struct AccessConfig { + /// Access type granted. + pub permission: AccessType, + + /// Account address with the associated permission. + pub address: AccountId, +} + +impl TryFrom for AccessConfig { + type Error = ErrorReport; + + fn try_from(proto: proto::cosmwasm::wasm::v1::AccessConfig) -> Result { + AccessConfig::try_from(&proto) + } +} + +impl TryFrom<&proto::cosmwasm::wasm::v1::AccessConfig> for AccessConfig { + type Error = ErrorReport; + + fn try_from(proto: &proto::cosmwasm::wasm::v1::AccessConfig) -> Result { + Ok(AccessConfig { + permission: AccessType::from_i32(proto.permission).ok_or(Error::InvalidEnumValue { + name: "permission", + found_value: proto.permission, + })?, + address: proto.address.parse()?, + }) + } +} + +impl From for proto::cosmwasm::wasm::v1::AccessConfig { + fn from(config: AccessConfig) -> proto::cosmwasm::wasm::v1::AccessConfig { + proto::cosmwasm::wasm::v1::AccessConfig::from(&config) + } +} + +impl From<&AccessConfig> for proto::cosmwasm::wasm::v1::AccessConfig { + fn from(config: &AccessConfig) -> proto::cosmwasm::wasm::v1::AccessConfig { + proto::cosmwasm::wasm::v1::AccessConfig { + permission: config.permission as i32, + address: config.address.to_string(), + } + } +} diff --git a/cosmrs/src/cosmwasm/code_info_response.rs b/cosmrs/src/cosmwasm/code_info_response.rs new file mode 100644 index 00000000..64fa1172 --- /dev/null +++ b/cosmrs/src/cosmwasm/code_info_response.rs @@ -0,0 +1,37 @@ +use super::ContractCodeId; +use crate::{proto, AccountId, ErrorReport, Result}; + +/// CodeInfoResponse contains code meta data from CodeInfo +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +pub struct CodeInfoResponse { + /// CodeId of the stored contract code. + pub code_id: ContractCodeId, + + /// Bech32 [`AccountId`] of the creator of this smart contract. + pub creator: AccountId, + + /// sha256 hash of the code stored + pub data_hash: Vec, +} + +impl TryFrom for CodeInfoResponse { + type Error = ErrorReport; + + fn try_from(proto: proto::cosmwasm::wasm::v1::CodeInfoResponse) -> Result { + Ok(CodeInfoResponse { + code_id: proto.code_id, + creator: proto.creator.parse()?, + data_hash: proto.data_hash, + }) + } +} + +impl From for proto::cosmwasm::wasm::v1::CodeInfoResponse { + fn from(code_info: CodeInfoResponse) -> Self { + proto::cosmwasm::wasm::v1::CodeInfoResponse { + code_id: code_info.code_id, + creator: code_info.creator.to_string(), + data_hash: code_info.data_hash, + } + } +} diff --git a/cosmrs/src/cosmwasm/contract_code_history_entry.rs b/cosmrs/src/cosmwasm/contract_code_history_entry.rs new file mode 100644 index 00000000..06324a32 --- /dev/null +++ b/cosmrs/src/cosmwasm/contract_code_history_entry.rs @@ -0,0 +1,41 @@ +use super::{AbsoluteTxPosition, ContractCodeId}; +use crate::{ + proto::{self, cosmwasm::wasm::v1::ContractCodeHistoryOperationType}, + Error, ErrorReport, Result, +}; + +/// ContractCodeHistoryEntry metadata to a contract. +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +pub struct ContractCodeHistoryEntry { + /// The source of this history entry. + pub operation: ContractCodeHistoryOperationType, + + /// Reference to the stored Wasm code. + pub code_id: ContractCodeId, + + /// Updated Tx position when the operation was executed. + pub updated: Option, + + /// Raw message returned by a wasm contract. + pub msg: Vec, +} + +impl TryFrom for ContractCodeHistoryEntry { + type Error = ErrorReport; + + fn try_from( + proto: proto::cosmwasm::wasm::v1::ContractCodeHistoryEntry, + ) -> Result { + Ok(ContractCodeHistoryEntry { + operation: ContractCodeHistoryOperationType::from_i32(proto.operation).ok_or( + Error::InvalidEnumValue { + name: "operation", + found_value: proto.operation, + }, + )?, + code_id: proto.code_id, + updated: None, + msg: vec![], + }) + } +} diff --git a/cosmrs/src/cosmwasm/contract_info.rs b/cosmrs/src/cosmwasm/contract_info.rs new file mode 100644 index 00000000..20e83128 --- /dev/null +++ b/cosmrs/src/cosmwasm/contract_info.rs @@ -0,0 +1,62 @@ +use super::{AbsoluteTxPosition, ContractCodeId}; +use crate::{ + proto::{self, traits::ParseOptional}, + AccountId, ErrorReport, Result, +}; + +/// ContractInfo stores a WASM contract instance +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +pub struct ContractInfo { + /// Reference to the stored Wasm code. + pub code_id: ContractCodeId, + + /// Creator address who initially instantiated the contract. + pub creator: AccountId, + + /// Admin is an optional address that can execute migrations. + pub admin: Option, + + /// Label is optional metadata to be stored with a contract instance. + pub label: String, + + /// Created Tx position when the contract was instantiated. + // Note that this data should kept internal and not be exposed via query results. + // Just use for sorting. + pub created: Option, + + /// The IBC port ID assigned to this contract by wasmd. + /// This is set for all IBC contracts (). + pub ibc_port_id: String, +} + +impl TryFrom for ContractInfo { + type Error = ErrorReport; + + fn try_from(proto: proto::cosmwasm::wasm::v1::ContractInfo) -> Result { + Ok(ContractInfo { + code_id: proto.code_id, + creator: proto.creator.parse()?, + admin: proto.admin.parse_optional()?, + label: proto.label, + created: proto.created.map(TryFrom::try_from).transpose()?, + ibc_port_id: proto.ibc_port_id, + }) + } +} + +impl From for proto::cosmwasm::wasm::v1::ContractInfo { + fn from(info: ContractInfo) -> Self { + proto::cosmwasm::wasm::v1::ContractInfo { + code_id: info.code_id, + creator: info.creator.to_string(), + admin: info + .admin + .map(|admin| admin.to_string()) + .unwrap_or_default(), + label: info.label, + created: info.created.map(Into::into), + ibc_port_id: info.ibc_port_id, + extension: None, + } + } +} diff --git a/cosmrs/src/cosmwasm/msg_clear_admin.rs b/cosmrs/src/cosmwasm/msg_clear_admin.rs new file mode 100644 index 00000000..c5b72d58 --- /dev/null +++ b/cosmrs/src/cosmwasm/msg_clear_admin.rs @@ -0,0 +1,73 @@ +use crate::{proto, tx::Msg, AccountId, ErrorReport, Result}; + +/// MsgClearAdmin removes any admin stored for a smart contract +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +pub struct MsgClearAdmin { + /// Sender is the that actor that signed the messages + pub sender: AccountId, + + /// Contract is the address of the smart contract + pub contract: AccountId, +} + +impl Msg for MsgClearAdmin { + type Proto = proto::cosmwasm::wasm::v1::MsgClearAdmin; +} + +impl TryFrom for MsgClearAdmin { + type Error = ErrorReport; + + fn try_from(proto: proto::cosmwasm::wasm::v1::MsgClearAdmin) -> Result { + MsgClearAdmin::try_from(&proto) + } +} + +impl TryFrom<&proto::cosmwasm::wasm::v1::MsgClearAdmin> for MsgClearAdmin { + type Error = ErrorReport; + + fn try_from(proto: &proto::cosmwasm::wasm::v1::MsgClearAdmin) -> Result { + Ok(MsgClearAdmin { + sender: proto.sender.parse()?, + contract: proto.contract.parse()?, + }) + } +} + +impl From for proto::cosmwasm::wasm::v1::MsgClearAdmin { + fn from(msg: MsgClearAdmin) -> proto::cosmwasm::wasm::v1::MsgClearAdmin { + proto::cosmwasm::wasm::v1::MsgClearAdmin::from(&msg) + } +} + +impl From<&MsgClearAdmin> for proto::cosmwasm::wasm::v1::MsgClearAdmin { + fn from(msg: &MsgClearAdmin) -> proto::cosmwasm::wasm::v1::MsgClearAdmin { + proto::cosmwasm::wasm::v1::MsgClearAdmin { + sender: msg.sender.to_string(), + contract: msg.contract.to_string(), + } + } +} + +/// MsgClearAdminResponse returns empty data +#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)] +pub struct MsgClearAdminResponse {} + +impl Msg for MsgClearAdminResponse { + type Proto = proto::cosmwasm::wasm::v1::MsgClearAdminResponse; +} + +impl TryFrom for MsgClearAdminResponse { + type Error = ErrorReport; + + fn try_from( + _proto: proto::cosmwasm::wasm::v1::MsgClearAdminResponse, + ) -> Result { + Ok(MsgClearAdminResponse {}) + } +} + +impl From for proto::cosmwasm::wasm::v1::MsgClearAdminResponse { + fn from(_msg: MsgClearAdminResponse) -> proto::cosmwasm::wasm::v1::MsgClearAdminResponse { + proto::cosmwasm::wasm::v1::MsgClearAdminResponse {} + } +} diff --git a/cosmrs/src/cosmwasm/msg_execute_contract.rs b/cosmrs/src/cosmwasm/msg_execute_contract.rs new file mode 100644 index 00000000..416a973e --- /dev/null +++ b/cosmrs/src/cosmwasm/msg_execute_contract.rs @@ -0,0 +1,81 @@ +use crate::{proto, tx::Msg, AccountId, Coin, ErrorReport, Result}; +use std::convert::TryFrom; + +/// MsgExecuteContract submits the given message data to a smart contract +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +pub struct MsgExecuteContract { + /// Sender is the that actor that signed the messages + pub sender: AccountId, + + /// Contract is the address of the smart contract + pub contract: AccountId, + + /// Msg json encoded message to be passed to the contract + pub msg: Vec, + + /// Funds coins that are transferred to the contract on execution + pub funds: Vec, +} + +impl Msg for MsgExecuteContract { + type Proto = proto::cosmwasm::wasm::v1::MsgExecuteContract; +} + +impl TryFrom for MsgExecuteContract { + type Error = ErrorReport; + + fn try_from( + proto: proto::cosmwasm::wasm::v1::MsgExecuteContract, + ) -> Result { + Ok(MsgExecuteContract { + sender: proto.sender.parse()?, + contract: proto.contract.parse()?, + msg: proto.msg.into_iter().map(Into::into).collect(), + funds: proto + .funds + .iter() + .map(TryFrom::try_from) + .collect::>()?, + }) + } +} + +impl From for proto::cosmwasm::wasm::v1::MsgExecuteContract { + fn from(msg: MsgExecuteContract) -> proto::cosmwasm::wasm::v1::MsgExecuteContract { + proto::cosmwasm::wasm::v1::MsgExecuteContract { + sender: msg.sender.to_string(), + contract: msg.contract.to_string(), + msg: msg.msg, + funds: msg.funds.iter().map(Into::into).collect(), + } + } +} + +/// MsgExecuteContractResponse returns execution result data. +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +pub struct MsgExecuteContractResponse { + /// Data contains base64-encoded bytes to returned from the contract + pub data: Vec, +} + +impl Msg for MsgExecuteContractResponse { + type Proto = proto::cosmwasm::wasm::v1::MsgExecuteContractResponse; +} + +impl TryFrom for MsgExecuteContractResponse { + type Error = ErrorReport; + + fn try_from( + proto: proto::cosmwasm::wasm::v1::MsgExecuteContractResponse, + ) -> Result { + Ok(MsgExecuteContractResponse { data: proto.data }) + } +} + +impl From for proto::cosmwasm::wasm::v1::MsgExecuteContractResponse { + fn from( + msg: MsgExecuteContractResponse, + ) -> proto::cosmwasm::wasm::v1::MsgExecuteContractResponse { + proto::cosmwasm::wasm::v1::MsgExecuteContractResponse { data: msg.data } + } +} diff --git a/cosmrs/src/cosmwasm/msg_initiate_contract.rs b/cosmrs/src/cosmwasm/msg_initiate_contract.rs new file mode 100644 index 00000000..7d867918 --- /dev/null +++ b/cosmrs/src/cosmwasm/msg_initiate_contract.rs @@ -0,0 +1,108 @@ +use crate::{ + proto::{self, traits::ParseOptional}, + tx::Msg, + AccountId, Coin, ErrorReport, Result, +}; + +/// MsgInstantiateContract create a new smart contract instance for the given +/// code id. +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +pub struct MsgInstantiateContract { + /// Sender is the that actor that signed the messages + pub sender: AccountId, + + /// Admin is an optional address that can execute migrations + pub admin: Option, + + /// CodeID is the reference to the stored WASM code + pub code_id: u64, + + /// Label is optional metadata to be stored with a contract instance. + pub label: Option, + + /// Msg json encoded message to be passed to the contract on instantiation + pub msg: Vec, + + /// Funds coins that are transferred to the contract on instantiation + pub funds: Vec, +} + +impl Msg for MsgInstantiateContract { + type Proto = proto::cosmwasm::wasm::v1::MsgInstantiateContract; +} + +impl TryFrom for MsgInstantiateContract { + type Error = ErrorReport; + + fn try_from( + proto: proto::cosmwasm::wasm::v1::MsgInstantiateContract, + ) -> Result { + Ok(MsgInstantiateContract { + sender: proto.sender.parse()?, + admin: proto.admin.parse_optional()?, + code_id: proto.code_id, + label: proto.label.parse_optional()?, + msg: proto.msg, + funds: proto + .funds + .into_iter() + .map(TryFrom::try_from) + .collect::>()?, + }) + } +} + +impl From for proto::cosmwasm::wasm::v1::MsgInstantiateContract { + fn from(msg: MsgInstantiateContract) -> proto::cosmwasm::wasm::v1::MsgInstantiateContract { + proto::cosmwasm::wasm::v1::MsgInstantiateContract { + sender: msg.sender.to_string(), + admin: msg.admin.map(|admin| admin.to_string()).unwrap_or_default(), + code_id: msg.code_id, + label: msg.label.unwrap_or_default(), + msg: msg.msg, + funds: msg.funds.into_iter().map(Into::into).collect(), + } + } +} + +/// MsgInstantiateContractResponse return instantiation result data +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +pub struct MsgInstantiateContractResponse { + /// Address is the bech32 address of the new contract instance. + pub address: AccountId, + + /// Data contains base64-encoded bytes to returned from the contract + pub data: Vec, +} + +impl Msg for MsgInstantiateContractResponse { + type Proto = proto::cosmwasm::wasm::v1::MsgInstantiateContractResponse; +} + +impl TryFrom + for MsgInstantiateContractResponse +{ + type Error = ErrorReport; + + fn try_from( + proto: proto::cosmwasm::wasm::v1::MsgInstantiateContractResponse, + ) -> Result { + Ok(MsgInstantiateContractResponse { + address: proto.address.parse()?, + data: proto.data, + }) + } +} + +impl From + for proto::cosmwasm::wasm::v1::MsgInstantiateContractResponse +{ + fn from( + msg: MsgInstantiateContractResponse, + ) -> proto::cosmwasm::wasm::v1::MsgInstantiateContractResponse { + proto::cosmwasm::wasm::v1::MsgInstantiateContractResponse { + address: msg.address.to_string(), + data: msg.data, + } + } +} diff --git a/cosmrs/src/cosmwasm/msg_migrate_contract.rs b/cosmrs/src/cosmwasm/msg_migrate_contract.rs new file mode 100644 index 00000000..98e397bc --- /dev/null +++ b/cosmrs/src/cosmwasm/msg_migrate_contract.rs @@ -0,0 +1,78 @@ +use crate::{proto, tx::Msg, AccountId, ErrorReport, Result}; +use std::convert::TryFrom; + +/// MsgMigrateContract runs a code upgrade/ downgrade for a smart contract +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +pub struct MsgMigrateContract { + /// Sender is the that actor that signed the messages + pub sender: AccountId, + + /// Contract is the address of the smart contract + pub contract: AccountId, + + /// CodeID references the new WASM code + pub code_id: u64, + + /// Msg json encoded message to be passed to the contract on migration + pub msg: Vec, +} + +impl Msg for MsgMigrateContract { + type Proto = proto::cosmwasm::wasm::v1::MsgMigrateContract; +} + +impl TryFrom for MsgMigrateContract { + type Error = ErrorReport; + + fn try_from( + proto: proto::cosmwasm::wasm::v1::MsgMigrateContract, + ) -> Result { + Ok(MsgMigrateContract { + sender: proto.sender.parse()?, + contract: proto.contract.parse()?, + code_id: proto.code_id, + msg: proto.msg, + }) + } +} + +impl From for proto::cosmwasm::wasm::v1::MsgMigrateContract { + fn from(msg: MsgMigrateContract) -> proto::cosmwasm::wasm::v1::MsgMigrateContract { + proto::cosmwasm::wasm::v1::MsgMigrateContract { + sender: msg.sender.to_string(), + contract: msg.contract.to_string(), + code_id: msg.code_id, + msg: msg.msg, + } + } +} + +/// MsgMigrateContractResponse returns contract migration result data. +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +pub struct MsgMigrateContractResponse { + /// Data contains same raw bytes returned as data from the wasm contract. + /// (May be empty) + pub data: Vec, +} + +impl Msg for MsgMigrateContractResponse { + type Proto = proto::cosmwasm::wasm::v1::MsgMigrateContractResponse; +} + +impl TryFrom for MsgMigrateContractResponse { + type Error = ErrorReport; + + fn try_from( + proto: proto::cosmwasm::wasm::v1::MsgMigrateContractResponse, + ) -> Result { + Ok(MsgMigrateContractResponse { data: proto.data }) + } +} + +impl From for proto::cosmwasm::wasm::v1::MsgMigrateContractResponse { + fn from( + msg: MsgMigrateContractResponse, + ) -> proto::cosmwasm::wasm::v1::MsgMigrateContractResponse { + proto::cosmwasm::wasm::v1::MsgMigrateContractResponse { data: msg.data } + } +} diff --git a/cosmrs/src/cosmwasm/msg_store_code.rs b/cosmrs/src/cosmwasm/msg_store_code.rs new file mode 100644 index 00000000..d4115599 --- /dev/null +++ b/cosmrs/src/cosmwasm/msg_store_code.rs @@ -0,0 +1,76 @@ +use super::AccessConfig; +use crate::{proto, tx::Msg, AccountId, ErrorReport, Result}; + +/// MsgStoreCode submit Wasm code to the system +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +pub struct MsgStoreCode { + /// Sender is the that actor that signed the messages + pub sender: AccountId, + + /// WASMByteCode can be raw or gzip compressed + pub wasm_byte_code: Vec, + + /// InstantiatePermission access control to apply on contract creation, + /// optional + pub instantiate_permission: Option, +} + +impl Msg for MsgStoreCode { + type Proto = proto::cosmwasm::wasm::v1::MsgStoreCode; +} + +impl TryFrom for MsgStoreCode { + type Error = ErrorReport; + + fn try_from(proto: proto::cosmwasm::wasm::v1::MsgStoreCode) -> Result { + Ok(MsgStoreCode { + sender: proto.sender.parse()?, + wasm_byte_code: proto.wasm_byte_code, + instantiate_permission: proto + .instantiate_permission + .map(TryFrom::try_from) + .transpose()?, + }) + } +} + +impl From for proto::cosmwasm::wasm::v1::MsgStoreCode { + fn from(msg: MsgStoreCode) -> proto::cosmwasm::wasm::v1::MsgStoreCode { + proto::cosmwasm::wasm::v1::MsgStoreCode { + sender: msg.sender.to_string(), + wasm_byte_code: msg.wasm_byte_code, + instantiate_permission: msg.instantiate_permission.map(Into::into), + } + } +} + +/// MsgStoreCodeResponse returns store result data. +#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)] +pub struct MsgStoreCodeResponse { + /// CodeID is the reference to the stored WASM code + pub code_id: u64, +} + +impl Msg for MsgStoreCodeResponse { + type Proto = proto::cosmwasm::wasm::v1::MsgStoreCodeResponse; +} + +impl TryFrom for MsgStoreCodeResponse { + type Error = ErrorReport; + + fn try_from( + proto: proto::cosmwasm::wasm::v1::MsgStoreCodeResponse, + ) -> Result { + Ok(MsgStoreCodeResponse { + code_id: proto.code_id, + }) + } +} + +impl From for proto::cosmwasm::wasm::v1::MsgStoreCodeResponse { + fn from(msg: MsgStoreCodeResponse) -> proto::cosmwasm::wasm::v1::MsgStoreCodeResponse { + proto::cosmwasm::wasm::v1::MsgStoreCodeResponse { + code_id: msg.code_id, + } + } +} diff --git a/cosmrs/src/cosmwasm/msg_update_admin.rs b/cosmrs/src/cosmwasm/msg_update_admin.rs new file mode 100644 index 00000000..01da434f --- /dev/null +++ b/cosmrs/src/cosmwasm/msg_update_admin.rs @@ -0,0 +1,78 @@ +use crate::{proto, tx::Msg, AccountId, ErrorReport, Result}; + +/// MsgUpdateAdmin sets a new admin for a smart contract +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +pub struct MsgUpdateAdmin { + /// Sender is the that actor that signed the messages + pub sender: AccountId, + + /// NewAdmin address to be set + pub new_admin: AccountId, + + /// Contract is the address of the smart contract + pub contract: AccountId, +} + +impl Msg for MsgUpdateAdmin { + type Proto = proto::cosmwasm::wasm::v1::MsgUpdateAdmin; +} + +impl TryFrom for MsgUpdateAdmin { + type Error = ErrorReport; + + fn try_from(proto: proto::cosmwasm::wasm::v1::MsgUpdateAdmin) -> Result { + MsgUpdateAdmin::try_from(&proto) + } +} + +impl TryFrom<&proto::cosmwasm::wasm::v1::MsgUpdateAdmin> for MsgUpdateAdmin { + type Error = ErrorReport; + + fn try_from(proto: &proto::cosmwasm::wasm::v1::MsgUpdateAdmin) -> Result { + Ok(MsgUpdateAdmin { + sender: proto.sender.parse()?, + new_admin: proto.new_admin.parse()?, + contract: proto.contract.parse()?, + }) + } +} + +impl From for proto::cosmwasm::wasm::v1::MsgUpdateAdmin { + fn from(msg: MsgUpdateAdmin) -> proto::cosmwasm::wasm::v1::MsgUpdateAdmin { + proto::cosmwasm::wasm::v1::MsgUpdateAdmin::from(&msg) + } +} + +impl From<&MsgUpdateAdmin> for proto::cosmwasm::wasm::v1::MsgUpdateAdmin { + fn from(msg: &MsgUpdateAdmin) -> proto::cosmwasm::wasm::v1::MsgUpdateAdmin { + proto::cosmwasm::wasm::v1::MsgUpdateAdmin { + sender: msg.sender.to_string(), + new_admin: msg.new_admin.to_string(), + contract: msg.contract.to_string(), + } + } +} + +/// MsgUpdateAdminResponse returns empty data +#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)] +pub struct MsgUpdateAdminResponse {} + +impl Msg for MsgUpdateAdminResponse { + type Proto = proto::cosmwasm::wasm::v1::MsgUpdateAdminResponse; +} + +impl TryFrom for MsgUpdateAdminResponse { + type Error = ErrorReport; + + fn try_from( + _proto: proto::cosmwasm::wasm::v1::MsgUpdateAdminResponse, + ) -> Result { + Ok(MsgUpdateAdminResponse {}) + } +} + +impl From for proto::cosmwasm::wasm::v1::MsgUpdateAdminResponse { + fn from(_msg: MsgUpdateAdminResponse) -> proto::cosmwasm::wasm::v1::MsgUpdateAdminResponse { + proto::cosmwasm::wasm::v1::MsgUpdateAdminResponse {} + } +} diff --git a/cosmrs/src/cosmwasm/query_code_response.rs b/cosmrs/src/cosmwasm/query_code_response.rs new file mode 100644 index 00000000..7a4cea62 --- /dev/null +++ b/cosmrs/src/cosmwasm/query_code_response.rs @@ -0,0 +1,32 @@ +use super::CodeInfoResponse; +use crate::{proto, ErrorReport, Result}; + +/// QueryCodeResponse is the response type for the Query/Code RPC method. +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +pub struct QueryCodeResponse { + /// If available, the associated code ID metadata. + pub code_info: Option, + + /// The original wasm bytes. + pub data: Vec, +} + +impl TryFrom for QueryCodeResponse { + type Error = ErrorReport; + + fn try_from(proto: proto::cosmwasm::wasm::v1::QueryCodeResponse) -> Result { + Ok(QueryCodeResponse { + code_info: proto.code_info.map(TryFrom::try_from).transpose()?, + data: proto.data, + }) + } +} + +impl From for proto::cosmwasm::wasm::v1::QueryCodeResponse { + fn from(response: QueryCodeResponse) -> Self { + proto::cosmwasm::wasm::v1::QueryCodeResponse { + code_info: response.code_info.map(Into::into), + data: response.data, + } + } +} diff --git a/cosmrs/src/distribution.rs b/cosmrs/src/distribution.rs index 5ffedb51..25402c9c 100644 --- a/cosmrs/src/distribution.rs +++ b/cosmrs/src/distribution.rs @@ -2,257 +2,13 @@ //! //! -use crate::{proto, tx::Msg, AccountId, Coin, ErrorReport, Result}; - -/// MsgSetWithdrawAddress represents a message to set a withdraw address for staking rewards. -#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] -pub struct MsgSetWithdrawAddress { - /// Delegator's address. - pub delegator_address: AccountId, - - /// withdraw address. - pub withdraw_address: AccountId, -} - -impl Msg for MsgSetWithdrawAddress { - type Proto = proto::cosmos::distribution::v1beta1::MsgSetWithdrawAddress; -} - -impl TryFrom - for MsgSetWithdrawAddress -{ - type Error = ErrorReport; - - fn try_from( - proto: proto::cosmos::distribution::v1beta1::MsgSetWithdrawAddress, - ) -> Result { - MsgSetWithdrawAddress::try_from(&proto) - } -} - -impl TryFrom<&proto::cosmos::distribution::v1beta1::MsgSetWithdrawAddress> - for MsgSetWithdrawAddress -{ - type Error = ErrorReport; - - fn try_from( - proto: &proto::cosmos::distribution::v1beta1::MsgSetWithdrawAddress, - ) -> Result { - Ok(MsgSetWithdrawAddress { - delegator_address: proto.delegator_address.parse()?, - withdraw_address: proto.withdraw_address.parse()?, - }) - } -} - -impl From for proto::cosmos::distribution::v1beta1::MsgSetWithdrawAddress { - fn from( - coin: MsgSetWithdrawAddress, - ) -> proto::cosmos::distribution::v1beta1::MsgSetWithdrawAddress { - proto::cosmos::distribution::v1beta1::MsgSetWithdrawAddress::from(&coin) - } -} - -impl From<&MsgSetWithdrawAddress> for proto::cosmos::distribution::v1beta1::MsgSetWithdrawAddress { - fn from( - msg: &MsgSetWithdrawAddress, - ) -> proto::cosmos::distribution::v1beta1::MsgSetWithdrawAddress { - proto::cosmos::distribution::v1beta1::MsgSetWithdrawAddress { - delegator_address: msg.delegator_address.to_string(), - withdraw_address: msg.withdraw_address.to_string(), - } - } -} - -/// MsgWithdrawDelegatorReward represents a message to withdraw a delegator's reward from a validator. -#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] -pub struct MsgWithdrawDelegatorReward { - /// Delegator's address. - pub delegator_address: AccountId, - - /// Validator's address. - pub validator_address: AccountId, -} - -impl Msg for MsgWithdrawDelegatorReward { - type Proto = proto::cosmos::distribution::v1beta1::MsgWithdrawDelegatorReward; -} - -impl TryFrom - for MsgWithdrawDelegatorReward -{ - type Error = ErrorReport; - - fn try_from( - proto: proto::cosmos::distribution::v1beta1::MsgWithdrawDelegatorReward, - ) -> Result { - MsgWithdrawDelegatorReward::try_from(&proto) - } -} - -impl TryFrom<&proto::cosmos::distribution::v1beta1::MsgWithdrawDelegatorReward> - for MsgWithdrawDelegatorReward -{ - type Error = ErrorReport; - - fn try_from( - proto: &proto::cosmos::distribution::v1beta1::MsgWithdrawDelegatorReward, - ) -> Result { - Ok(MsgWithdrawDelegatorReward { - delegator_address: proto.delegator_address.parse()?, - validator_address: proto.validator_address.parse()?, - }) - } -} - -impl From - for proto::cosmos::distribution::v1beta1::MsgWithdrawDelegatorReward -{ - fn from( - coin: MsgWithdrawDelegatorReward, - ) -> proto::cosmos::distribution::v1beta1::MsgWithdrawDelegatorReward { - proto::cosmos::distribution::v1beta1::MsgWithdrawDelegatorReward::from(&coin) - } -} - -impl From<&MsgWithdrawDelegatorReward> - for proto::cosmos::distribution::v1beta1::MsgWithdrawDelegatorReward -{ - fn from( - msg: &MsgWithdrawDelegatorReward, - ) -> proto::cosmos::distribution::v1beta1::MsgWithdrawDelegatorReward { - proto::cosmos::distribution::v1beta1::MsgWithdrawDelegatorReward { - delegator_address: msg.delegator_address.to_string(), - validator_address: msg.validator_address.to_string(), - } - } -} - -/// WithdrawValidatorCommission represents a message to withdraw a validator's staking commission. -#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] -pub struct MsgWithdrawValidatorCommission { - /// Validator's address. - pub validator_address: AccountId, -} - -impl Msg for MsgWithdrawValidatorCommission { - type Proto = proto::cosmos::distribution::v1beta1::MsgWithdrawValidatorCommission; -} - -impl TryFrom - for MsgWithdrawValidatorCommission -{ - type Error = ErrorReport; - - fn try_from( - proto: proto::cosmos::distribution::v1beta1::MsgWithdrawValidatorCommission, - ) -> Result { - MsgWithdrawValidatorCommission::try_from(&proto) - } -} - -impl TryFrom<&proto::cosmos::distribution::v1beta1::MsgWithdrawValidatorCommission> - for MsgWithdrawValidatorCommission -{ - type Error = ErrorReport; - - fn try_from( - proto: &proto::cosmos::distribution::v1beta1::MsgWithdrawValidatorCommission, - ) -> Result { - Ok(MsgWithdrawValidatorCommission { - validator_address: proto.validator_address.parse()?, - }) - } -} - -impl From - for proto::cosmos::distribution::v1beta1::MsgWithdrawValidatorCommission -{ - fn from( - coin: MsgWithdrawValidatorCommission, - ) -> proto::cosmos::distribution::v1beta1::MsgWithdrawValidatorCommission { - proto::cosmos::distribution::v1beta1::MsgWithdrawValidatorCommission::from(&coin) - } -} - -impl From<&MsgWithdrawValidatorCommission> - for proto::cosmos::distribution::v1beta1::MsgWithdrawValidatorCommission -{ - fn from( - msg: &MsgWithdrawValidatorCommission, - ) -> proto::cosmos::distribution::v1beta1::MsgWithdrawValidatorCommission { - proto::cosmos::distribution::v1beta1::MsgWithdrawValidatorCommission { - validator_address: msg.validator_address.to_string(), - } - } -} - -/// MsgFundCommunityPool represents a message to send coins from depositor to the community pool. -#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] -pub struct MsgFundCommunityPool { - /// Depositor's address. - pub depositor: AccountId, - - /// Amount to deposit. - pub amount: Vec, -} - -impl Msg for MsgFundCommunityPool { - type Proto = proto::cosmos::distribution::v1beta1::MsgFundCommunityPool; -} - -impl TryFrom for MsgFundCommunityPool { - type Error = ErrorReport; - - fn try_from( - proto: proto::cosmos::distribution::v1beta1::MsgFundCommunityPool, - ) -> Result { - MsgFundCommunityPool::try_from(&proto) - } -} - -impl TryFrom<&proto::cosmos::distribution::v1beta1::MsgFundCommunityPool> for MsgFundCommunityPool { - type Error = ErrorReport; - - fn try_from( - proto: &proto::cosmos::distribution::v1beta1::MsgFundCommunityPool, - ) -> Result { - let mut amounts = vec![]; - for amount in proto.amount.iter() { - amounts.push(Coin { - denom: amount.denom.parse()?, - amount: amount.amount.parse()?, - }) - } - Ok(MsgFundCommunityPool { - depositor: proto.depositor.parse()?, - amount: amounts, - }) - } -} - -impl From for proto::cosmos::distribution::v1beta1::MsgFundCommunityPool { - fn from( - coin: MsgFundCommunityPool, - ) -> proto::cosmos::distribution::v1beta1::MsgFundCommunityPool { - proto::cosmos::distribution::v1beta1::MsgFundCommunityPool::from(&coin) - } -} - -impl From<&MsgFundCommunityPool> for proto::cosmos::distribution::v1beta1::MsgFundCommunityPool { - fn from( - msg: &MsgFundCommunityPool, - ) -> proto::cosmos::distribution::v1beta1::MsgFundCommunityPool { - let mut amounts = vec![]; - for amount in msg.amount.iter() { - amounts.push(proto::cosmos::base::v1beta1::Coin { - denom: amount.denom.to_string(), - amount: amount.amount.to_string(), - }) - } - proto::cosmos::distribution::v1beta1::MsgFundCommunityPool { - depositor: "".to_string(), - amount: amounts, - } - } -} +mod msg_fund_community_pool; +mod msg_set_withdraw_address; +mod msg_withdraw_delegator_reward; +mod msg_withdraw_validator_commission; + +pub use self::{ + msg_fund_community_pool::MsgFundCommunityPool, msg_set_withdraw_address::MsgSetWithdrawAddress, + msg_withdraw_delegator_reward::MsgWithdrawDelegatorReward, + msg_withdraw_validator_commission::MsgWithdrawValidatorCommission, +}; diff --git a/cosmrs/src/distribution/msg_fund_community_pool.rs b/cosmrs/src/distribution/msg_fund_community_pool.rs new file mode 100644 index 00000000..574991b9 --- /dev/null +++ b/cosmrs/src/distribution/msg_fund_community_pool.rs @@ -0,0 +1,71 @@ +use crate::{proto, tx::Msg, AccountId, Coin, ErrorReport, Result}; + +/// MsgFundCommunityPool represents a message to send coins from depositor to the community pool. +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +pub struct MsgFundCommunityPool { + /// Depositor's address. + pub depositor: AccountId, + + /// Amount to deposit. + pub amount: Vec, +} + +impl Msg for MsgFundCommunityPool { + type Proto = proto::cosmos::distribution::v1beta1::MsgFundCommunityPool; +} + +impl TryFrom for MsgFundCommunityPool { + type Error = ErrorReport; + + fn try_from( + proto: proto::cosmos::distribution::v1beta1::MsgFundCommunityPool, + ) -> Result { + MsgFundCommunityPool::try_from(&proto) + } +} + +impl TryFrom<&proto::cosmos::distribution::v1beta1::MsgFundCommunityPool> for MsgFundCommunityPool { + type Error = ErrorReport; + + fn try_from( + proto: &proto::cosmos::distribution::v1beta1::MsgFundCommunityPool, + ) -> Result { + let mut amounts = vec![]; + for amount in proto.amount.iter() { + amounts.push(Coin { + denom: amount.denom.parse()?, + amount: amount.amount.parse()?, + }) + } + Ok(MsgFundCommunityPool { + depositor: proto.depositor.parse()?, + amount: amounts, + }) + } +} + +impl From for proto::cosmos::distribution::v1beta1::MsgFundCommunityPool { + fn from( + coin: MsgFundCommunityPool, + ) -> proto::cosmos::distribution::v1beta1::MsgFundCommunityPool { + proto::cosmos::distribution::v1beta1::MsgFundCommunityPool::from(&coin) + } +} + +impl From<&MsgFundCommunityPool> for proto::cosmos::distribution::v1beta1::MsgFundCommunityPool { + fn from( + msg: &MsgFundCommunityPool, + ) -> proto::cosmos::distribution::v1beta1::MsgFundCommunityPool { + let mut amounts = vec![]; + for amount in msg.amount.iter() { + amounts.push(proto::cosmos::base::v1beta1::Coin { + denom: amount.denom.to_string(), + amount: amount.amount.to_string(), + }) + } + proto::cosmos::distribution::v1beta1::MsgFundCommunityPool { + depositor: "".to_string(), + amount: amounts, + } + } +} diff --git a/cosmrs/src/distribution/msg_set_withdraw_address.rs b/cosmrs/src/distribution/msg_set_withdraw_address.rs new file mode 100644 index 00000000..9ed39511 --- /dev/null +++ b/cosmrs/src/distribution/msg_set_withdraw_address.rs @@ -0,0 +1,61 @@ +use crate::{proto, tx::Msg, AccountId, ErrorReport, Result}; + +/// MsgSetWithdrawAddress represents a message to set a withdraw address for staking rewards. +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +pub struct MsgSetWithdrawAddress { + /// Delegator's address. + pub delegator_address: AccountId, + + /// withdraw address. + pub withdraw_address: AccountId, +} + +impl Msg for MsgSetWithdrawAddress { + type Proto = proto::cosmos::distribution::v1beta1::MsgSetWithdrawAddress; +} + +impl TryFrom + for MsgSetWithdrawAddress +{ + type Error = ErrorReport; + + fn try_from( + proto: proto::cosmos::distribution::v1beta1::MsgSetWithdrawAddress, + ) -> Result { + MsgSetWithdrawAddress::try_from(&proto) + } +} + +impl TryFrom<&proto::cosmos::distribution::v1beta1::MsgSetWithdrawAddress> + for MsgSetWithdrawAddress +{ + type Error = ErrorReport; + + fn try_from( + proto: &proto::cosmos::distribution::v1beta1::MsgSetWithdrawAddress, + ) -> Result { + Ok(MsgSetWithdrawAddress { + delegator_address: proto.delegator_address.parse()?, + withdraw_address: proto.withdraw_address.parse()?, + }) + } +} + +impl From for proto::cosmos::distribution::v1beta1::MsgSetWithdrawAddress { + fn from( + coin: MsgSetWithdrawAddress, + ) -> proto::cosmos::distribution::v1beta1::MsgSetWithdrawAddress { + proto::cosmos::distribution::v1beta1::MsgSetWithdrawAddress::from(&coin) + } +} + +impl From<&MsgSetWithdrawAddress> for proto::cosmos::distribution::v1beta1::MsgSetWithdrawAddress { + fn from( + msg: &MsgSetWithdrawAddress, + ) -> proto::cosmos::distribution::v1beta1::MsgSetWithdrawAddress { + proto::cosmos::distribution::v1beta1::MsgSetWithdrawAddress { + delegator_address: msg.delegator_address.to_string(), + withdraw_address: msg.withdraw_address.to_string(), + } + } +} diff --git a/cosmrs/src/distribution/msg_withdraw_delegator_reward.rs b/cosmrs/src/distribution/msg_withdraw_delegator_reward.rs new file mode 100644 index 00000000..ab95a345 --- /dev/null +++ b/cosmrs/src/distribution/msg_withdraw_delegator_reward.rs @@ -0,0 +1,65 @@ +use crate::{proto, tx::Msg, AccountId, ErrorReport, Result}; + +/// MsgWithdrawDelegatorReward represents a message to withdraw a delegator's reward from a validator. +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +pub struct MsgWithdrawDelegatorReward { + /// Delegator's address. + pub delegator_address: AccountId, + + /// Validator's address. + pub validator_address: AccountId, +} + +impl Msg for MsgWithdrawDelegatorReward { + type Proto = proto::cosmos::distribution::v1beta1::MsgWithdrawDelegatorReward; +} + +impl TryFrom + for MsgWithdrawDelegatorReward +{ + type Error = ErrorReport; + + fn try_from( + proto: proto::cosmos::distribution::v1beta1::MsgWithdrawDelegatorReward, + ) -> Result { + MsgWithdrawDelegatorReward::try_from(&proto) + } +} + +impl TryFrom<&proto::cosmos::distribution::v1beta1::MsgWithdrawDelegatorReward> + for MsgWithdrawDelegatorReward +{ + type Error = ErrorReport; + + fn try_from( + proto: &proto::cosmos::distribution::v1beta1::MsgWithdrawDelegatorReward, + ) -> Result { + Ok(MsgWithdrawDelegatorReward { + delegator_address: proto.delegator_address.parse()?, + validator_address: proto.validator_address.parse()?, + }) + } +} + +impl From + for proto::cosmos::distribution::v1beta1::MsgWithdrawDelegatorReward +{ + fn from( + coin: MsgWithdrawDelegatorReward, + ) -> proto::cosmos::distribution::v1beta1::MsgWithdrawDelegatorReward { + proto::cosmos::distribution::v1beta1::MsgWithdrawDelegatorReward::from(&coin) + } +} + +impl From<&MsgWithdrawDelegatorReward> + for proto::cosmos::distribution::v1beta1::MsgWithdrawDelegatorReward +{ + fn from( + msg: &MsgWithdrawDelegatorReward, + ) -> proto::cosmos::distribution::v1beta1::MsgWithdrawDelegatorReward { + proto::cosmos::distribution::v1beta1::MsgWithdrawDelegatorReward { + delegator_address: msg.delegator_address.to_string(), + validator_address: msg.validator_address.to_string(), + } + } +} diff --git a/cosmrs/src/distribution/msg_withdraw_validator_commission.rs b/cosmrs/src/distribution/msg_withdraw_validator_commission.rs new file mode 100644 index 00000000..96e3e1bd --- /dev/null +++ b/cosmrs/src/distribution/msg_withdraw_validator_commission.rs @@ -0,0 +1,60 @@ +use crate::{proto, tx::Msg, AccountId, ErrorReport, Result}; + +/// WithdrawValidatorCommission represents a message to withdraw a validator's staking commission. +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +pub struct MsgWithdrawValidatorCommission { + /// Validator's address. + pub validator_address: AccountId, +} + +impl Msg for MsgWithdrawValidatorCommission { + type Proto = proto::cosmos::distribution::v1beta1::MsgWithdrawValidatorCommission; +} + +impl TryFrom + for MsgWithdrawValidatorCommission +{ + type Error = ErrorReport; + + fn try_from( + proto: proto::cosmos::distribution::v1beta1::MsgWithdrawValidatorCommission, + ) -> Result { + MsgWithdrawValidatorCommission::try_from(&proto) + } +} + +impl TryFrom<&proto::cosmos::distribution::v1beta1::MsgWithdrawValidatorCommission> + for MsgWithdrawValidatorCommission +{ + type Error = ErrorReport; + + fn try_from( + proto: &proto::cosmos::distribution::v1beta1::MsgWithdrawValidatorCommission, + ) -> Result { + Ok(MsgWithdrawValidatorCommission { + validator_address: proto.validator_address.parse()?, + }) + } +} + +impl From + for proto::cosmos::distribution::v1beta1::MsgWithdrawValidatorCommission +{ + fn from( + coin: MsgWithdrawValidatorCommission, + ) -> proto::cosmos::distribution::v1beta1::MsgWithdrawValidatorCommission { + proto::cosmos::distribution::v1beta1::MsgWithdrawValidatorCommission::from(&coin) + } +} + +impl From<&MsgWithdrawValidatorCommission> + for proto::cosmos::distribution::v1beta1::MsgWithdrawValidatorCommission +{ + fn from( + msg: &MsgWithdrawValidatorCommission, + ) -> proto::cosmos::distribution::v1beta1::MsgWithdrawValidatorCommission { + proto::cosmos::distribution::v1beta1::MsgWithdrawValidatorCommission { + validator_address: msg.validator_address.to_string(), + } + } +} diff --git a/cosmrs/src/feegrant.rs b/cosmrs/src/feegrant.rs index 3593c58e..553c1e8e 100644 --- a/cosmrs/src/feegrant.rs +++ b/cosmrs/src/feegrant.rs @@ -2,308 +2,14 @@ //! //! -use crate::{proto, tx::Msg, AccountId, Any, Coin, ErrorReport, Result}; -use std::time::{Duration, SystemTime}; - -/// MsgGrantAllowance adds permission for Grantee to spend up to Allowance -/// of fees from the account of Granter. -#[derive(Clone, Debug, PartialEq)] -pub struct MsgGrantAllowance { - /// granter is the address of the user granting an allowance of their funds. - pub granter: AccountId, - - /// grantee is the address of the user being granted an allowance of another user's funds. - pub grantee: AccountId, - - /// allowance can be any of basic and filtered fee allowance. - pub allowance: Option, -} - -impl Msg for MsgGrantAllowance { - type Proto = proto::cosmos::feegrant::v1beta1::MsgGrantAllowance; -} - -impl TryFrom for MsgGrantAllowance { - type Error = ErrorReport; - - fn try_from( - proto: proto::cosmos::feegrant::v1beta1::MsgGrantAllowance, - ) -> Result { - MsgGrantAllowance::try_from(&proto) - } -} - -impl TryFrom<&proto::cosmos::feegrant::v1beta1::MsgGrantAllowance> for MsgGrantAllowance { - type Error = ErrorReport; - - fn try_from( - proto: &proto::cosmos::feegrant::v1beta1::MsgGrantAllowance, - ) -> Result { - Ok(MsgGrantAllowance { - granter: proto.granter.parse()?, - grantee: proto.grantee.parse()?, - allowance: proto.allowance.clone(), - }) - } -} - -impl From for proto::cosmos::feegrant::v1beta1::MsgGrantAllowance { - fn from(coin: MsgGrantAllowance) -> proto::cosmos::feegrant::v1beta1::MsgGrantAllowance { - proto::cosmos::feegrant::v1beta1::MsgGrantAllowance::from(&coin) - } -} - -impl From<&MsgGrantAllowance> for proto::cosmos::feegrant::v1beta1::MsgGrantAllowance { - fn from(msg: &MsgGrantAllowance) -> proto::cosmos::feegrant::v1beta1::MsgGrantAllowance { - proto::cosmos::feegrant::v1beta1::MsgGrantAllowance { - granter: msg.granter.to_string(), - grantee: msg.grantee.to_string(), - allowance: msg.allowance.clone(), - } - } -} - -/// MsgRevokeAllowance removes any existing Allowance from Granter to Grantee. -#[derive(Clone, Debug, PartialEq)] -pub struct MsgRevokeAllowance { - /// granter is the address of the user granting an allowance of their funds. - pub granter: AccountId, - - /// grantee is the address of the user being granted an allowance of another user's funds. - pub grantee: AccountId, -} - -impl Msg for MsgRevokeAllowance { - type Proto = proto::cosmos::feegrant::v1beta1::MsgRevokeAllowance; -} - -impl TryFrom for MsgRevokeAllowance { - type Error = ErrorReport; - - fn try_from( - proto: proto::cosmos::feegrant::v1beta1::MsgRevokeAllowance, - ) -> Result { - MsgRevokeAllowance::try_from(&proto) - } -} - -impl TryFrom<&proto::cosmos::feegrant::v1beta1::MsgRevokeAllowance> for MsgRevokeAllowance { - type Error = ErrorReport; - - fn try_from( - proto: &proto::cosmos::feegrant::v1beta1::MsgRevokeAllowance, - ) -> Result { - Ok(MsgRevokeAllowance { - granter: proto.granter.parse()?, - grantee: proto.grantee.parse()?, - }) - } -} - -impl From for proto::cosmos::feegrant::v1beta1::MsgRevokeAllowance { - fn from(allowance: MsgRevokeAllowance) -> proto::cosmos::feegrant::v1beta1::MsgRevokeAllowance { - proto::cosmos::feegrant::v1beta1::MsgRevokeAllowance::from(&allowance) - } -} - -impl From<&MsgRevokeAllowance> for proto::cosmos::feegrant::v1beta1::MsgRevokeAllowance { - fn from(msg: &MsgRevokeAllowance) -> proto::cosmos::feegrant::v1beta1::MsgRevokeAllowance { - proto::cosmos::feegrant::v1beta1::MsgRevokeAllowance { - granter: msg.granter.to_string(), - grantee: msg.grantee.to_string(), - } - } -} - -/// BasicAllowance implements Allowance with a one-time grant of tokens -/// that optionally expires. The grantee can use up to SpendLimit to cover fees. -#[derive(Clone, Debug, PartialEq)] -pub struct BasicAllowance { - /// spend_limit specifies the maximum amount of tokens that can be spent - /// by this allowance and will be updated as tokens are spent. If it is - /// empty, there is no spend limit and any amount of coins can be spent. - pub spend_limit: Vec, - - /// expiration specifies an optional time when this allowance expires - pub expiration: Option, -} - -impl Msg for BasicAllowance { - type Proto = proto::cosmos::feegrant::v1beta1::BasicAllowance; -} - -impl TryFrom for BasicAllowance { - type Error = ErrorReport; - - fn try_from(proto: proto::cosmos::feegrant::v1beta1::BasicAllowance) -> Result { - BasicAllowance::try_from(&proto) - } -} - -impl TryFrom<&proto::cosmos::feegrant::v1beta1::BasicAllowance> for BasicAllowance { - type Error = ErrorReport; - - fn try_from( - proto: &proto::cosmos::feegrant::v1beta1::BasicAllowance, - ) -> Result { - Ok(BasicAllowance { - spend_limit: proto - .spend_limit - .iter() - .map(TryFrom::try_from) - .collect::>()?, - expiration: proto - .expiration - .clone() - .map(TryFrom::try_from) - .transpose()?, - }) - } -} - -impl From for proto::cosmos::feegrant::v1beta1::BasicAllowance { - fn from(allowance: BasicAllowance) -> proto::cosmos::feegrant::v1beta1::BasicAllowance { - proto::cosmos::feegrant::v1beta1::BasicAllowance::from(&allowance) - } -} - -impl From<&BasicAllowance> for proto::cosmos::feegrant::v1beta1::BasicAllowance { - fn from(allowance: &BasicAllowance) -> proto::cosmos::feegrant::v1beta1::BasicAllowance { - proto::cosmos::feegrant::v1beta1::BasicAllowance { - spend_limit: allowance.spend_limit.iter().map(Into::into).collect(), - expiration: allowance.expiration.map(Into::into), - } - } -} - -/// PeriodicAllowance extends Allowance to allow for both a maximum cap, -/// as well as a limit per time period. -#[derive(Clone, Debug, PartialEq)] -pub struct PeriodicAllowance { - /// basic specifies a struct of `BasicAllowance` - pub basic: Option, - - /// period specifies the time duration in which period_spend_limit coins can - /// be spent before that allowance is reset - pub period: Option, - - /// period_spend_limit specifies the maximum number of coins that can be spent - /// in the period - pub period_spend_limit: Vec, - - /// period_can_spend is the number of coins left to be spent before the period_reset time - pub period_can_spend: Vec, - - /// period_reset is the time at which this period resets and a new one begins, - /// it is calculated from the start time of the first transaction after the - /// last period ended - pub period_reset: Option, -} - -impl Msg for PeriodicAllowance { - type Proto = proto::cosmos::feegrant::v1beta1::PeriodicAllowance; -} - -impl TryFrom for PeriodicAllowance { - type Error = ErrorReport; - - fn try_from( - proto: proto::cosmos::feegrant::v1beta1::PeriodicAllowance, - ) -> Result { - Ok(PeriodicAllowance { - basic: proto.basic.map(TryFrom::try_from).transpose()?, - period: proto.period.map(TryFrom::try_from).transpose()?, - period_spend_limit: proto - .period_spend_limit - .iter() - .map(TryFrom::try_from) - .collect::>()?, - period_can_spend: proto - .period_can_spend - .iter() - .map(TryFrom::try_from) - .collect::>()?, - period_reset: proto.period_reset.map(TryFrom::try_from).transpose()?, - }) - } -} - -impl From for proto::cosmos::feegrant::v1beta1::PeriodicAllowance { - fn from(allowance: PeriodicAllowance) -> proto::cosmos::feegrant::v1beta1::PeriodicAllowance { - proto::cosmos::feegrant::v1beta1::PeriodicAllowance { - basic: allowance.basic.map(Into::into), - period: allowance - .period - .map(TryInto::try_into) - .transpose() - .expect("invalid allowance period"), // TODO(tarcieri): fallible serialization? - period_spend_limit: allowance - .period_spend_limit - .iter() - .map(Into::into) - .collect(), - period_can_spend: allowance.period_can_spend.iter().map(Into::into).collect(), - period_reset: allowance.period_reset.map(Into::into), - } - } -} - -/// AllowedMsgAllowance creates allowance only for specified message types. -#[derive(Clone, Debug, PartialEq)] -pub struct AllowedMsgAllowance { - /// allowance can be any of basic and filtered fee allowance. - pub allowance: Option, - - /// allowed_messages are the messages for which the grantee has the access. - pub allowed_messages: Vec, -} - -impl Msg for AllowedMsgAllowance { - type Proto = proto::cosmos::feegrant::v1beta1::AllowedMsgAllowance; -} - -impl TryFrom for AllowedMsgAllowance { - type Error = ErrorReport; - - fn try_from( - proto: proto::cosmos::feegrant::v1beta1::AllowedMsgAllowance, - ) -> Result { - AllowedMsgAllowance::try_from(&proto) - } -} - -impl TryFrom<&proto::cosmos::feegrant::v1beta1::AllowedMsgAllowance> for AllowedMsgAllowance { - type Error = ErrorReport; - - fn try_from( - proto: &proto::cosmos::feegrant::v1beta1::AllowedMsgAllowance, - ) -> Result { - Ok(AllowedMsgAllowance { - allowance: proto.allowance.clone(), - allowed_messages: proto - .allowed_messages - .iter() - .map(TryFrom::try_from) - .collect::>()?, - }) - } -} - -impl From for proto::cosmos::feegrant::v1beta1::AllowedMsgAllowance { - fn from( - allowance: AllowedMsgAllowance, - ) -> proto::cosmos::feegrant::v1beta1::AllowedMsgAllowance { - proto::cosmos::feegrant::v1beta1::AllowedMsgAllowance::from(&allowance) - } -} - -impl From<&AllowedMsgAllowance> for proto::cosmos::feegrant::v1beta1::AllowedMsgAllowance { - fn from( - allowance: &AllowedMsgAllowance, - ) -> proto::cosmos::feegrant::v1beta1::AllowedMsgAllowance { - proto::cosmos::feegrant::v1beta1::AllowedMsgAllowance { - allowance: allowance.allowance.clone().map(Into::into), - allowed_messages: allowance.allowed_messages.iter().map(Into::into).collect(), - } - } -} +mod allowed_msg_allowance; +mod basic_allowance; +mod msg_grant_allowance; +mod msg_revoke_allowance; +mod periodic_allowance; + +pub use self::{ + allowed_msg_allowance::AllowedMsgAllowance, basic_allowance::BasicAllowance, + msg_grant_allowance::MsgGrantAllowance, msg_revoke_allowance::MsgRevokeAllowance, + periodic_allowance::PeriodicAllowance, +}; diff --git a/cosmrs/src/feegrant/allowed_msg_allowance.rs b/cosmrs/src/feegrant/allowed_msg_allowance.rs new file mode 100644 index 00000000..d09a94a9 --- /dev/null +++ b/cosmrs/src/feegrant/allowed_msg_allowance.rs @@ -0,0 +1,61 @@ +use crate::{proto, tx::Msg, Any, ErrorReport, Result}; + +/// AllowedMsgAllowance creates allowance only for specified message types. +#[derive(Clone, Debug, PartialEq)] +pub struct AllowedMsgAllowance { + /// allowance can be any of basic and filtered fee allowance. + pub allowance: Option, + + /// allowed_messages are the messages for which the grantee has the access. + pub allowed_messages: Vec, +} + +impl Msg for AllowedMsgAllowance { + type Proto = proto::cosmos::feegrant::v1beta1::AllowedMsgAllowance; +} + +impl TryFrom for AllowedMsgAllowance { + type Error = ErrorReport; + + fn try_from( + proto: proto::cosmos::feegrant::v1beta1::AllowedMsgAllowance, + ) -> Result { + AllowedMsgAllowance::try_from(&proto) + } +} + +impl TryFrom<&proto::cosmos::feegrant::v1beta1::AllowedMsgAllowance> for AllowedMsgAllowance { + type Error = ErrorReport; + + fn try_from( + proto: &proto::cosmos::feegrant::v1beta1::AllowedMsgAllowance, + ) -> Result { + Ok(AllowedMsgAllowance { + allowance: proto.allowance.clone(), + allowed_messages: proto + .allowed_messages + .iter() + .map(TryFrom::try_from) + .collect::>()?, + }) + } +} + +impl From for proto::cosmos::feegrant::v1beta1::AllowedMsgAllowance { + fn from( + allowance: AllowedMsgAllowance, + ) -> proto::cosmos::feegrant::v1beta1::AllowedMsgAllowance { + proto::cosmos::feegrant::v1beta1::AllowedMsgAllowance::from(&allowance) + } +} + +impl From<&AllowedMsgAllowance> for proto::cosmos::feegrant::v1beta1::AllowedMsgAllowance { + fn from( + allowance: &AllowedMsgAllowance, + ) -> proto::cosmos::feegrant::v1beta1::AllowedMsgAllowance { + proto::cosmos::feegrant::v1beta1::AllowedMsgAllowance { + allowance: allowance.allowance.clone().map(Into::into), + allowed_messages: allowance.allowed_messages.iter().map(Into::into).collect(), + } + } +} diff --git a/cosmrs/src/feegrant/basic_allowance.rs b/cosmrs/src/feegrant/basic_allowance.rs new file mode 100644 index 00000000..60b77d11 --- /dev/null +++ b/cosmrs/src/feegrant/basic_allowance.rs @@ -0,0 +1,63 @@ +use crate::{proto, tx::Msg, Coin, ErrorReport, Result}; +use std::time::SystemTime; + +/// BasicAllowance implements Allowance with a one-time grant of tokens +/// that optionally expires. The grantee can use up to SpendLimit to cover fees. +#[derive(Clone, Debug, PartialEq)] +pub struct BasicAllowance { + /// spend_limit specifies the maximum amount of tokens that can be spent + /// by this allowance and will be updated as tokens are spent. If it is + /// empty, there is no spend limit and any amount of coins can be spent. + pub spend_limit: Vec, + + /// expiration specifies an optional time when this allowance expires + pub expiration: Option, +} + +impl Msg for BasicAllowance { + type Proto = proto::cosmos::feegrant::v1beta1::BasicAllowance; +} + +impl TryFrom for BasicAllowance { + type Error = ErrorReport; + + fn try_from(proto: proto::cosmos::feegrant::v1beta1::BasicAllowance) -> Result { + BasicAllowance::try_from(&proto) + } +} + +impl TryFrom<&proto::cosmos::feegrant::v1beta1::BasicAllowance> for BasicAllowance { + type Error = ErrorReport; + + fn try_from( + proto: &proto::cosmos::feegrant::v1beta1::BasicAllowance, + ) -> Result { + Ok(BasicAllowance { + spend_limit: proto + .spend_limit + .iter() + .map(TryFrom::try_from) + .collect::>()?, + expiration: proto + .expiration + .clone() + .map(TryFrom::try_from) + .transpose()?, + }) + } +} + +impl From for proto::cosmos::feegrant::v1beta1::BasicAllowance { + fn from(allowance: BasicAllowance) -> proto::cosmos::feegrant::v1beta1::BasicAllowance { + proto::cosmos::feegrant::v1beta1::BasicAllowance::from(&allowance) + } +} + +impl From<&BasicAllowance> for proto::cosmos::feegrant::v1beta1::BasicAllowance { + fn from(allowance: &BasicAllowance) -> proto::cosmos::feegrant::v1beta1::BasicAllowance { + proto::cosmos::feegrant::v1beta1::BasicAllowance { + spend_limit: allowance.spend_limit.iter().map(Into::into).collect(), + expiration: allowance.expiration.map(Into::into), + } + } +} diff --git a/cosmrs/src/feegrant/msg_grant_allowance.rs b/cosmrs/src/feegrant/msg_grant_allowance.rs new file mode 100644 index 00000000..dcbb589c --- /dev/null +++ b/cosmrs/src/feegrant/msg_grant_allowance.rs @@ -0,0 +1,59 @@ +use crate::{proto, tx::Msg, AccountId, Any, ErrorReport, Result}; + +/// MsgGrantAllowance adds permission for Grantee to spend up to Allowance +/// of fees from the account of Granter. +#[derive(Clone, Debug, PartialEq)] +pub struct MsgGrantAllowance { + /// granter is the address of the user granting an allowance of their funds. + pub granter: AccountId, + + /// grantee is the address of the user being granted an allowance of another user's funds. + pub grantee: AccountId, + + /// allowance can be any of basic and filtered fee allowance. + pub allowance: Option, +} + +impl Msg for MsgGrantAllowance { + type Proto = proto::cosmos::feegrant::v1beta1::MsgGrantAllowance; +} + +impl TryFrom for MsgGrantAllowance { + type Error = ErrorReport; + + fn try_from( + proto: proto::cosmos::feegrant::v1beta1::MsgGrantAllowance, + ) -> Result { + MsgGrantAllowance::try_from(&proto) + } +} + +impl TryFrom<&proto::cosmos::feegrant::v1beta1::MsgGrantAllowance> for MsgGrantAllowance { + type Error = ErrorReport; + + fn try_from( + proto: &proto::cosmos::feegrant::v1beta1::MsgGrantAllowance, + ) -> Result { + Ok(MsgGrantAllowance { + granter: proto.granter.parse()?, + grantee: proto.grantee.parse()?, + allowance: proto.allowance.clone(), + }) + } +} + +impl From for proto::cosmos::feegrant::v1beta1::MsgGrantAllowance { + fn from(coin: MsgGrantAllowance) -> proto::cosmos::feegrant::v1beta1::MsgGrantAllowance { + proto::cosmos::feegrant::v1beta1::MsgGrantAllowance::from(&coin) + } +} + +impl From<&MsgGrantAllowance> for proto::cosmos::feegrant::v1beta1::MsgGrantAllowance { + fn from(msg: &MsgGrantAllowance) -> proto::cosmos::feegrant::v1beta1::MsgGrantAllowance { + proto::cosmos::feegrant::v1beta1::MsgGrantAllowance { + granter: msg.granter.to_string(), + grantee: msg.grantee.to_string(), + allowance: msg.allowance.clone(), + } + } +} diff --git a/cosmrs/src/feegrant/msg_revoke_allowance.rs b/cosmrs/src/feegrant/msg_revoke_allowance.rs new file mode 100644 index 00000000..b41d7ccf --- /dev/null +++ b/cosmrs/src/feegrant/msg_revoke_allowance.rs @@ -0,0 +1,53 @@ +use crate::{proto, tx::Msg, AccountId, ErrorReport, Result}; + +/// MsgRevokeAllowance removes any existing Allowance from Granter to Grantee. +#[derive(Clone, Debug, PartialEq)] +pub struct MsgRevokeAllowance { + /// granter is the address of the user granting an allowance of their funds. + pub granter: AccountId, + + /// grantee is the address of the user being granted an allowance of another user's funds. + pub grantee: AccountId, +} + +impl Msg for MsgRevokeAllowance { + type Proto = proto::cosmos::feegrant::v1beta1::MsgRevokeAllowance; +} + +impl TryFrom for MsgRevokeAllowance { + type Error = ErrorReport; + + fn try_from( + proto: proto::cosmos::feegrant::v1beta1::MsgRevokeAllowance, + ) -> Result { + MsgRevokeAllowance::try_from(&proto) + } +} + +impl TryFrom<&proto::cosmos::feegrant::v1beta1::MsgRevokeAllowance> for MsgRevokeAllowance { + type Error = ErrorReport; + + fn try_from( + proto: &proto::cosmos::feegrant::v1beta1::MsgRevokeAllowance, + ) -> Result { + Ok(MsgRevokeAllowance { + granter: proto.granter.parse()?, + grantee: proto.grantee.parse()?, + }) + } +} + +impl From for proto::cosmos::feegrant::v1beta1::MsgRevokeAllowance { + fn from(allowance: MsgRevokeAllowance) -> proto::cosmos::feegrant::v1beta1::MsgRevokeAllowance { + proto::cosmos::feegrant::v1beta1::MsgRevokeAllowance::from(&allowance) + } +} + +impl From<&MsgRevokeAllowance> for proto::cosmos::feegrant::v1beta1::MsgRevokeAllowance { + fn from(msg: &MsgRevokeAllowance) -> proto::cosmos::feegrant::v1beta1::MsgRevokeAllowance { + proto::cosmos::feegrant::v1beta1::MsgRevokeAllowance { + granter: msg.granter.to_string(), + grantee: msg.grantee.to_string(), + } + } +} diff --git a/cosmrs/src/feegrant/periodic_allowance.rs b/cosmrs/src/feegrant/periodic_allowance.rs new file mode 100644 index 00000000..4b230809 --- /dev/null +++ b/cosmrs/src/feegrant/periodic_allowance.rs @@ -0,0 +1,75 @@ +use super::BasicAllowance; +use crate::{proto, tx::Msg, Coin, ErrorReport, Result}; +use std::time::{Duration, SystemTime}; + +/// PeriodicAllowance extends Allowance to allow for both a maximum cap, +/// as well as a limit per time period. +#[derive(Clone, Debug, PartialEq)] +pub struct PeriodicAllowance { + /// basic specifies a struct of `BasicAllowance` + pub basic: Option, + + /// period specifies the time duration in which period_spend_limit coins can + /// be spent before that allowance is reset + pub period: Option, + + /// period_spend_limit specifies the maximum number of coins that can be spent + /// in the period + pub period_spend_limit: Vec, + + /// period_can_spend is the number of coins left to be spent before the period_reset time + pub period_can_spend: Vec, + + /// period_reset is the time at which this period resets and a new one begins, + /// it is calculated from the start time of the first transaction after the + /// last period ended + pub period_reset: Option, +} + +impl Msg for PeriodicAllowance { + type Proto = proto::cosmos::feegrant::v1beta1::PeriodicAllowance; +} + +impl TryFrom for PeriodicAllowance { + type Error = ErrorReport; + + fn try_from( + proto: proto::cosmos::feegrant::v1beta1::PeriodicAllowance, + ) -> Result { + Ok(PeriodicAllowance { + basic: proto.basic.map(TryFrom::try_from).transpose()?, + period: proto.period.map(TryFrom::try_from).transpose()?, + period_spend_limit: proto + .period_spend_limit + .iter() + .map(TryFrom::try_from) + .collect::>()?, + period_can_spend: proto + .period_can_spend + .iter() + .map(TryFrom::try_from) + .collect::>()?, + period_reset: proto.period_reset.map(TryFrom::try_from).transpose()?, + }) + } +} + +impl From for proto::cosmos::feegrant::v1beta1::PeriodicAllowance { + fn from(allowance: PeriodicAllowance) -> proto::cosmos::feegrant::v1beta1::PeriodicAllowance { + proto::cosmos::feegrant::v1beta1::PeriodicAllowance { + basic: allowance.basic.map(Into::into), + period: allowance + .period + .map(TryInto::try_into) + .transpose() + .expect("invalid allowance period"), // TODO(tarcieri): fallible serialization? + period_spend_limit: allowance + .period_spend_limit + .iter() + .map(Into::into) + .collect(), + period_can_spend: allowance.period_can_spend.iter().map(Into::into).collect(), + period_reset: allowance.period_reset.map(Into::into), + } + } +} diff --git a/cosmrs/src/staking.rs b/cosmrs/src/staking.rs index 4b9f39f2..7fadb5f8 100644 --- a/cosmrs/src/staking.rs +++ b/cosmrs/src/staking.rs @@ -2,211 +2,11 @@ //! //! -use crate::{proto, tx::Msg, AccountId, Coin, Error, ErrorReport, Result}; - -/// MsgDelegate represents a message to delegate coins to a validator. -#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] -pub struct MsgDelegate { - /// Delegator's address. - pub delegator_address: AccountId, - - /// Validator's address. - pub validator_address: AccountId, - - /// Amount to send - pub amount: Coin, -} - -impl Msg for MsgDelegate { - type Proto = proto::cosmos::staking::v1beta1::MsgDelegate; -} - -impl TryFrom for MsgDelegate { - type Error = ErrorReport; - - fn try_from(proto: proto::cosmos::staking::v1beta1::MsgDelegate) -> Result { - MsgDelegate::try_from(&proto) - } -} - -impl TryFrom<&proto::cosmos::staking::v1beta1::MsgDelegate> for MsgDelegate { - type Error = ErrorReport; - - fn try_from(proto: &proto::cosmos::staking::v1beta1::MsgDelegate) -> Result { - let amount = proto - .amount - .as_ref() - .ok_or(Error::MissingField { name: "amount" })?; - - Ok(MsgDelegate { - delegator_address: proto.delegator_address.parse()?, - validator_address: proto.validator_address.parse()?, - amount: Coin { - denom: amount.denom.parse()?, - amount: amount.amount.parse()?, - }, - }) - } -} - -impl From for proto::cosmos::staking::v1beta1::MsgDelegate { - fn from(coin: MsgDelegate) -> proto::cosmos::staking::v1beta1::MsgDelegate { - proto::cosmos::staking::v1beta1::MsgDelegate::from(&coin) - } -} - -impl From<&MsgDelegate> for proto::cosmos::staking::v1beta1::MsgDelegate { - fn from(msg: &MsgDelegate) -> proto::cosmos::staking::v1beta1::MsgDelegate { - let amount = proto::cosmos::base::v1beta1::Coin { - denom: msg.amount.denom.to_string(), - amount: msg.amount.amount.to_string(), - }; - - proto::cosmos::staking::v1beta1::MsgDelegate { - delegator_address: msg.delegator_address.to_string(), - validator_address: msg.validator_address.to_string(), - amount: Some(amount), - } - } -} - -/// MsgUndelegate represents a message to undelegate coins from a validator. -#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] -pub struct MsgUndelegate { - /// Delegator's address. - pub delegator_address: AccountId, - - /// Validator's address. - pub validator_address: AccountId, - - /// Amount to UnDelegate - pub amount: Coin, -} - -impl Msg for MsgUndelegate { - type Proto = proto::cosmos::staking::v1beta1::MsgUndelegate; -} - -impl TryFrom for MsgUndelegate { - type Error = ErrorReport; - - fn try_from(proto: proto::cosmos::staking::v1beta1::MsgUndelegate) -> Result { - MsgUndelegate::try_from(&proto) - } -} - -impl TryFrom<&proto::cosmos::staking::v1beta1::MsgUndelegate> for MsgUndelegate { - type Error = ErrorReport; - - fn try_from(proto: &proto::cosmos::staking::v1beta1::MsgUndelegate) -> Result { - let amount = proto - .amount - .as_ref() - .ok_or(Error::MissingField { name: "amount" })?; - - Ok(MsgUndelegate { - delegator_address: proto.delegator_address.parse()?, - validator_address: proto.validator_address.parse()?, - amount: Coin { - denom: amount.denom.parse()?, - amount: amount.amount.parse()?, - }, - }) - } -} - -impl From for proto::cosmos::staking::v1beta1::MsgUndelegate { - fn from(coin: MsgUndelegate) -> proto::cosmos::staking::v1beta1::MsgUndelegate { - proto::cosmos::staking::v1beta1::MsgUndelegate::from(&coin) - } -} - -impl From<&MsgUndelegate> for proto::cosmos::staking::v1beta1::MsgUndelegate { - fn from(msg: &MsgUndelegate) -> proto::cosmos::staking::v1beta1::MsgUndelegate { - let amount = proto::cosmos::base::v1beta1::Coin { - denom: msg.amount.denom.to_string(), - amount: msg.amount.amount.to_string(), - }; - - proto::cosmos::staking::v1beta1::MsgUndelegate { - delegator_address: msg.delegator_address.to_string(), - validator_address: msg.validator_address.to_string(), - amount: Some(amount), - } - } -} - -/// MsgBeginRedelegate represents a message to redelegate coins from one validator to another. -#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] -pub struct MsgBeginRedelegate { - /// Delegator's address. - pub delegator_address: AccountId, - - /// Source validator's address. - pub validator_src_address: AccountId, - - /// Destination validator's address. - pub validator_dst_address: AccountId, - - /// Amount to UnDelegate - pub amount: Coin, -} - -impl Msg for MsgBeginRedelegate { - type Proto = proto::cosmos::staking::v1beta1::MsgBeginRedelegate; -} - -impl TryFrom for MsgBeginRedelegate { - type Error = ErrorReport; - - fn try_from( - proto: proto::cosmos::staking::v1beta1::MsgBeginRedelegate, - ) -> Result { - MsgBeginRedelegate::try_from(&proto) - } -} - -impl TryFrom<&proto::cosmos::staking::v1beta1::MsgBeginRedelegate> for MsgBeginRedelegate { - type Error = ErrorReport; - - fn try_from( - proto: &proto::cosmos::staking::v1beta1::MsgBeginRedelegate, - ) -> Result { - let amount = proto - .amount - .as_ref() - .ok_or(Error::MissingField { name: "amount" })?; - - Ok(MsgBeginRedelegate { - delegator_address: proto.delegator_address.parse()?, - validator_src_address: proto.validator_src_address.parse()?, - validator_dst_address: proto.validator_dst_address.parse()?, - amount: Coin { - denom: amount.denom.parse()?, - amount: amount.amount.parse()?, - }, - }) - } -} - -impl From for proto::cosmos::staking::v1beta1::MsgBeginRedelegate { - fn from(coin: MsgBeginRedelegate) -> proto::cosmos::staking::v1beta1::MsgBeginRedelegate { - proto::cosmos::staking::v1beta1::MsgBeginRedelegate::from(&coin) - } -} - -impl From<&MsgBeginRedelegate> for proto::cosmos::staking::v1beta1::MsgBeginRedelegate { - fn from(msg: &MsgBeginRedelegate) -> proto::cosmos::staking::v1beta1::MsgBeginRedelegate { - let amount = proto::cosmos::base::v1beta1::Coin { - denom: msg.amount.denom.to_string(), - amount: msg.amount.amount.to_string(), - }; - - proto::cosmos::staking::v1beta1::MsgBeginRedelegate { - delegator_address: msg.delegator_address.to_string(), - validator_src_address: msg.validator_src_address.to_string(), - validator_dst_address: msg.validator_dst_address.to_string(), - amount: Some(amount), - } - } -} +mod msg_begin_redelegate; +mod msg_delegate; +mod msg_undelegate; + +pub use self::{ + msg_begin_redelegate::MsgBeginRedelegate, msg_delegate::MsgDelegate, + msg_undelegate::MsgUndelegate, +}; diff --git a/cosmrs/src/staking/msg_begin_redelegate.rs b/cosmrs/src/staking/msg_begin_redelegate.rs new file mode 100644 index 00000000..35b86b40 --- /dev/null +++ b/cosmrs/src/staking/msg_begin_redelegate.rs @@ -0,0 +1,76 @@ +use crate::{proto, tx::Msg, AccountId, Coin, Error, ErrorReport, Result}; + +/// MsgBeginRedelegate represents a message to redelegate coins from one validator to another. +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +pub struct MsgBeginRedelegate { + /// Delegator's address. + pub delegator_address: AccountId, + + /// Source validator's address. + pub validator_src_address: AccountId, + + /// Destination validator's address. + pub validator_dst_address: AccountId, + + /// Amount to UnDelegate + pub amount: Coin, +} + +impl Msg for MsgBeginRedelegate { + type Proto = proto::cosmos::staking::v1beta1::MsgBeginRedelegate; +} + +impl TryFrom for MsgBeginRedelegate { + type Error = ErrorReport; + + fn try_from( + proto: proto::cosmos::staking::v1beta1::MsgBeginRedelegate, + ) -> Result { + MsgBeginRedelegate::try_from(&proto) + } +} + +impl TryFrom<&proto::cosmos::staking::v1beta1::MsgBeginRedelegate> for MsgBeginRedelegate { + type Error = ErrorReport; + + fn try_from( + proto: &proto::cosmos::staking::v1beta1::MsgBeginRedelegate, + ) -> Result { + let amount = proto + .amount + .as_ref() + .ok_or(Error::MissingField { name: "amount" })?; + + Ok(MsgBeginRedelegate { + delegator_address: proto.delegator_address.parse()?, + validator_src_address: proto.validator_src_address.parse()?, + validator_dst_address: proto.validator_dst_address.parse()?, + amount: Coin { + denom: amount.denom.parse()?, + amount: amount.amount.parse()?, + }, + }) + } +} + +impl From for proto::cosmos::staking::v1beta1::MsgBeginRedelegate { + fn from(coin: MsgBeginRedelegate) -> proto::cosmos::staking::v1beta1::MsgBeginRedelegate { + proto::cosmos::staking::v1beta1::MsgBeginRedelegate::from(&coin) + } +} + +impl From<&MsgBeginRedelegate> for proto::cosmos::staking::v1beta1::MsgBeginRedelegate { + fn from(msg: &MsgBeginRedelegate) -> proto::cosmos::staking::v1beta1::MsgBeginRedelegate { + let amount = proto::cosmos::base::v1beta1::Coin { + denom: msg.amount.denom.to_string(), + amount: msg.amount.amount.to_string(), + }; + + proto::cosmos::staking::v1beta1::MsgBeginRedelegate { + delegator_address: msg.delegator_address.to_string(), + validator_src_address: msg.validator_src_address.to_string(), + validator_dst_address: msg.validator_dst_address.to_string(), + amount: Some(amount), + } + } +} diff --git a/cosmrs/src/staking/msg_delegate.rs b/cosmrs/src/staking/msg_delegate.rs new file mode 100644 index 00000000..c105e79d --- /dev/null +++ b/cosmrs/src/staking/msg_delegate.rs @@ -0,0 +1,67 @@ +use crate::{proto, tx::Msg, AccountId, Coin, Error, ErrorReport, Result}; + +/// MsgDelegate represents a message to delegate coins to a validator. +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +pub struct MsgDelegate { + /// Delegator's address. + pub delegator_address: AccountId, + + /// Validator's address. + pub validator_address: AccountId, + + /// Amount to send + pub amount: Coin, +} + +impl Msg for MsgDelegate { + type Proto = proto::cosmos::staking::v1beta1::MsgDelegate; +} + +impl TryFrom for MsgDelegate { + type Error = ErrorReport; + + fn try_from(proto: proto::cosmos::staking::v1beta1::MsgDelegate) -> Result { + MsgDelegate::try_from(&proto) + } +} + +impl TryFrom<&proto::cosmos::staking::v1beta1::MsgDelegate> for MsgDelegate { + type Error = ErrorReport; + + fn try_from(proto: &proto::cosmos::staking::v1beta1::MsgDelegate) -> Result { + let amount = proto + .amount + .as_ref() + .ok_or(Error::MissingField { name: "amount" })?; + + Ok(MsgDelegate { + delegator_address: proto.delegator_address.parse()?, + validator_address: proto.validator_address.parse()?, + amount: Coin { + denom: amount.denom.parse()?, + amount: amount.amount.parse()?, + }, + }) + } +} + +impl From for proto::cosmos::staking::v1beta1::MsgDelegate { + fn from(coin: MsgDelegate) -> proto::cosmos::staking::v1beta1::MsgDelegate { + proto::cosmos::staking::v1beta1::MsgDelegate::from(&coin) + } +} + +impl From<&MsgDelegate> for proto::cosmos::staking::v1beta1::MsgDelegate { + fn from(msg: &MsgDelegate) -> proto::cosmos::staking::v1beta1::MsgDelegate { + let amount = proto::cosmos::base::v1beta1::Coin { + denom: msg.amount.denom.to_string(), + amount: msg.amount.amount.to_string(), + }; + + proto::cosmos::staking::v1beta1::MsgDelegate { + delegator_address: msg.delegator_address.to_string(), + validator_address: msg.validator_address.to_string(), + amount: Some(amount), + } + } +} diff --git a/cosmrs/src/staking/msg_undelegate.rs b/cosmrs/src/staking/msg_undelegate.rs new file mode 100644 index 00000000..6e839bff --- /dev/null +++ b/cosmrs/src/staking/msg_undelegate.rs @@ -0,0 +1,67 @@ +use crate::{proto, tx::Msg, AccountId, Coin, Error, ErrorReport, Result}; + +/// MsgUndelegate represents a message to undelegate coins from a validator. +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +pub struct MsgUndelegate { + /// Delegator's address. + pub delegator_address: AccountId, + + /// Validator's address. + pub validator_address: AccountId, + + /// Amount to UnDelegate + pub amount: Coin, +} + +impl Msg for MsgUndelegate { + type Proto = proto::cosmos::staking::v1beta1::MsgUndelegate; +} + +impl TryFrom for MsgUndelegate { + type Error = ErrorReport; + + fn try_from(proto: proto::cosmos::staking::v1beta1::MsgUndelegate) -> Result { + MsgUndelegate::try_from(&proto) + } +} + +impl TryFrom<&proto::cosmos::staking::v1beta1::MsgUndelegate> for MsgUndelegate { + type Error = ErrorReport; + + fn try_from(proto: &proto::cosmos::staking::v1beta1::MsgUndelegate) -> Result { + let amount = proto + .amount + .as_ref() + .ok_or(Error::MissingField { name: "amount" })?; + + Ok(MsgUndelegate { + delegator_address: proto.delegator_address.parse()?, + validator_address: proto.validator_address.parse()?, + amount: Coin { + denom: amount.denom.parse()?, + amount: amount.amount.parse()?, + }, + }) + } +} + +impl From for proto::cosmos::staking::v1beta1::MsgUndelegate { + fn from(coin: MsgUndelegate) -> proto::cosmos::staking::v1beta1::MsgUndelegate { + proto::cosmos::staking::v1beta1::MsgUndelegate::from(&coin) + } +} + +impl From<&MsgUndelegate> for proto::cosmos::staking::v1beta1::MsgUndelegate { + fn from(msg: &MsgUndelegate) -> proto::cosmos::staking::v1beta1::MsgUndelegate { + let amount = proto::cosmos::base::v1beta1::Coin { + denom: msg.amount.denom.to_string(), + amount: msg.amount.amount.to_string(), + }; + + proto::cosmos::staking::v1beta1::MsgUndelegate { + delegator_address: msg.delegator_address.to_string(), + validator_address: msg.validator_address.to_string(), + amount: Some(amount), + } + } +} diff --git a/cosmrs/src/vesting.rs b/cosmrs/src/vesting.rs index a1fcd1e7..b4d67ac2 100644 --- a/cosmrs/src/vesting.rs +++ b/cosmrs/src/vesting.rs @@ -1,262 +1,15 @@ //! Vesting-related types -use crate::auth::BaseAccount; -use crate::Result; -use crate::{proto, Coin, ErrorReport}; - -/// BaseVestingAccount implements the VestingAccount interface. It contains all -/// the necessary fields needed for any vesting account implementation. -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct BaseVestingAccount { - /// [`BaseAccount`] specification of this vesting account. - pub base_account: Option, - - /// The amount of coins (per denomination) that are initially part of a vesting account. - /// These coins are set at genesis. - pub original_vesting: Vec, - - /// The tracked amount of coins (per denomination) that are delegated from a vesting account - /// that have been fully vested at time of delegation. - pub delegated_free: Vec, - - /// The tracked amount of coins (per denomination) that are delegated from a vesting account - /// that were vesting at time of delegation. - pub delegated_vesting: Vec, - - /// The BFT time at which a vesting account is fully vested - pub end_time: i64, -} - -impl TryFrom for BaseVestingAccount { - type Error = ErrorReport; - - fn try_from( - proto: proto::cosmos::vesting::v1beta1::BaseVestingAccount, - ) -> Result { - Ok(BaseVestingAccount { - base_account: proto.base_account.map(TryFrom::try_from).transpose()?, - original_vesting: proto - .original_vesting - .into_iter() - .map(TryFrom::try_from) - .collect::>()?, - delegated_free: proto - .delegated_free - .into_iter() - .map(TryFrom::try_from) - .collect::>()?, - delegated_vesting: proto - .delegated_vesting - .into_iter() - .map(TryFrom::try_from) - .collect::>()?, - end_time: proto.end_time, - }) - } -} - -impl From for proto::cosmos::vesting::v1beta1::BaseVestingAccount { - fn from(account: BaseVestingAccount) -> Self { - proto::cosmos::vesting::v1beta1::BaseVestingAccount { - base_account: account.base_account.map(Into::into), - original_vesting: account - .original_vesting - .into_iter() - .map(Into::into) - .collect(), - delegated_free: account.delegated_free.into_iter().map(Into::into).collect(), - delegated_vesting: account - .delegated_vesting - .into_iter() - .map(Into::into) - .collect(), - end_time: account.end_time, - } - } -} - -/// ContinuousVestingAccount implements the VestingAccount interface. It -/// continuously vests by unlocking coins linearly with respect to time. -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct ContinuousVestingAccount { - /// Base vesting account specification required for this vesting implementation. - pub base_vesting_account: Option, - - /// The BFT time at which a vesting account starts to vest. - pub start_time: i64, -} - -impl TryFrom - for ContinuousVestingAccount -{ - type Error = ErrorReport; - - fn try_from( - proto: proto::cosmos::vesting::v1beta1::ContinuousVestingAccount, - ) -> Result { - Ok(ContinuousVestingAccount { - base_vesting_account: proto - .base_vesting_account - .map(TryFrom::try_from) - .transpose()?, - start_time: proto.start_time, - }) - } -} - -impl From for proto::cosmos::vesting::v1beta1::ContinuousVestingAccount { - fn from(account: ContinuousVestingAccount) -> Self { - proto::cosmos::vesting::v1beta1::ContinuousVestingAccount { - base_vesting_account: account.base_vesting_account.map(Into::into), - start_time: 0, - } - } -} - -/// DelayedVestingAccount implements the VestingAccount interface. It vests all -/// coins after a specific time, but non prior. In other words, it keeps them -/// locked until a specified time. -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct DelayedVestingAccount { - /// Base vesting account specification required for this vesting implementation. - pub base_vesting_account: Option, -} - -impl TryFrom for DelayedVestingAccount { - type Error = ErrorReport; - - fn try_from( - proto: proto::cosmos::vesting::v1beta1::DelayedVestingAccount, - ) -> Result { - Ok(DelayedVestingAccount { - base_vesting_account: proto - .base_vesting_account - .map(TryFrom::try_from) - .transpose()?, - }) - } -} - -impl From for proto::cosmos::vesting::v1beta1::DelayedVestingAccount { - fn from(account: DelayedVestingAccount) -> Self { - proto::cosmos::vesting::v1beta1::DelayedVestingAccount { - base_vesting_account: account.base_vesting_account.map(Into::into), - } - } -} - -/// Period defines a length of time and amount of coins that will vest. -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct Period { - /// Length of this vesting period in seconds. - pub length: i64, - - /// The amount of coins (per denomination) that will vest upon this period finishing. - pub amount: Vec, -} - -impl TryFrom for Period { - type Error = ErrorReport; - - fn try_from(proto: proto::cosmos::vesting::v1beta1::Period) -> Result { - Ok(Period { - length: proto.length, - amount: proto - .amount - .into_iter() - .map(TryFrom::try_from) - .collect::>()?, - }) - } -} - -impl From for proto::cosmos::vesting::v1beta1::Period { - fn from(period: Period) -> Self { - proto::cosmos::vesting::v1beta1::Period { - length: period.length, - amount: period.amount.into_iter().map(Into::into).collect(), - } - } -} - -/// PeriodicVestingAccount implements the VestingAccount interface. It -/// periodically vests by unlocking coins during each specified period. -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct PeriodicVestingAccount { - /// Base vesting account specification required for this vesting implementation. - pub base_vesting_account: Option, - - /// The BFT time at which a vesting account starts to vest. - pub start_time: i64, - - /// Vesting [`Period`]s associated with this account. Periods are sequential, - /// in that the duration of a period only starts at the end of the previous period. - pub vesting_periods: Vec, -} - -impl TryFrom for PeriodicVestingAccount { - type Error = ErrorReport; - - fn try_from( - proto: proto::cosmos::vesting::v1beta1::PeriodicVestingAccount, - ) -> Result { - Ok(PeriodicVestingAccount { - base_vesting_account: proto - .base_vesting_account - .map(TryFrom::try_from) - .transpose()?, - start_time: proto.start_time, - vesting_periods: proto - .vesting_periods - .into_iter() - .map(TryFrom::try_from) - .collect::>()?, - }) - } -} - -impl From for proto::cosmos::vesting::v1beta1::PeriodicVestingAccount { - fn from(account: PeriodicVestingAccount) -> Self { - proto::cosmos::vesting::v1beta1::PeriodicVestingAccount { - base_vesting_account: account.base_vesting_account.map(Into::into), - start_time: account.start_time, - vesting_periods: account - .vesting_periods - .into_iter() - .map(Into::into) - .collect(), - } - } -} - -/// PermanentLockedAccount implements the VestingAccount interface. It does -/// not ever release coins, locking them indefinitely. Coins in this account can -/// still be used for delegating and for governance votes even while locked. -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct PermanentLockedAccount { - /// Base vesting account specification required for this vesting implementation. - pub base_vesting_account: Option, -} - -impl TryFrom for PermanentLockedAccount { - type Error = ErrorReport; - - fn try_from( - proto: proto::cosmos::vesting::v1beta1::PermanentLockedAccount, - ) -> Result { - Ok(PermanentLockedAccount { - base_vesting_account: proto - .base_vesting_account - .map(TryFrom::try_from) - .transpose()?, - }) - } -} - -impl From for proto::cosmos::vesting::v1beta1::PermanentLockedAccount { - fn from(account: PermanentLockedAccount) -> Self { - proto::cosmos::vesting::v1beta1::PermanentLockedAccount { - base_vesting_account: account.base_vesting_account.map(Into::into), - } - } -} +mod base_vesting_account; +mod continuous_vesting_account; +mod delayed_vesting_account; +mod period; +mod periodic_vesting_account; +mod permanent_locked_account; + +pub use self::{ + base_vesting_account::BaseVestingAccount, continuous_vesting_account::ContinuousVestingAccount, + delayed_vesting_account::DelayedVestingAccount, period::Period, + periodic_vesting_account::PeriodicVestingAccount, + permanent_locked_account::PermanentLockedAccount, +}; diff --git a/cosmrs/src/vesting/base_vesting_account.rs b/cosmrs/src/vesting/base_vesting_account.rs new file mode 100644 index 00000000..930ed273 --- /dev/null +++ b/cosmrs/src/vesting/base_vesting_account.rs @@ -0,0 +1,73 @@ +use crate::{auth::BaseAccount, proto, Coin, ErrorReport, Result}; + +/// [`BaseVestingAccount`] implements the `VestingAccount` interface. +/// +/// It contains all the necessary fields needed for any vesting account implementation. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct BaseVestingAccount { + /// [`BaseAccount`] specification of this vesting account. + pub base_account: Option, + + /// The amount of coins (per denomination) that are initially part of a vesting account. + /// These coins are set at genesis. + pub original_vesting: Vec, + + /// The tracked amount of coins (per denomination) that are delegated from a vesting account + /// that have been fully vested at time of delegation. + pub delegated_free: Vec, + + /// The tracked amount of coins (per denomination) that are delegated from a vesting account + /// that were vesting at time of delegation. + pub delegated_vesting: Vec, + + /// The BFT time at which a vesting account is fully vested + pub end_time: i64, +} + +impl TryFrom for BaseVestingAccount { + type Error = ErrorReport; + + fn try_from( + proto: proto::cosmos::vesting::v1beta1::BaseVestingAccount, + ) -> Result { + Ok(BaseVestingAccount { + base_account: proto.base_account.map(TryFrom::try_from).transpose()?, + original_vesting: proto + .original_vesting + .into_iter() + .map(TryFrom::try_from) + .collect::>()?, + delegated_free: proto + .delegated_free + .into_iter() + .map(TryFrom::try_from) + .collect::>()?, + delegated_vesting: proto + .delegated_vesting + .into_iter() + .map(TryFrom::try_from) + .collect::>()?, + end_time: proto.end_time, + }) + } +} + +impl From for proto::cosmos::vesting::v1beta1::BaseVestingAccount { + fn from(account: BaseVestingAccount) -> Self { + proto::cosmos::vesting::v1beta1::BaseVestingAccount { + base_account: account.base_account.map(Into::into), + original_vesting: account + .original_vesting + .into_iter() + .map(Into::into) + .collect(), + delegated_free: account.delegated_free.into_iter().map(Into::into).collect(), + delegated_vesting: account + .delegated_vesting + .into_iter() + .map(Into::into) + .collect(), + end_time: account.end_time, + } + } +} diff --git a/cosmrs/src/vesting/continuous_vesting_account.rs b/cosmrs/src/vesting/continuous_vesting_account.rs new file mode 100644 index 00000000..f641b32e --- /dev/null +++ b/cosmrs/src/vesting/continuous_vesting_account.rs @@ -0,0 +1,41 @@ +use super::BaseVestingAccount; +use crate::{proto, ErrorReport, Result}; + +/// [`ContinuousVestingAccount`] implements the `VestingAccount` interface. +/// +/// It continuously vests by unlocking coins linearly with respect to time. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct ContinuousVestingAccount { + /// Base vesting account specification required for this vesting implementation. + pub base_vesting_account: Option, + + /// The BFT time at which a vesting account starts to vest. + pub start_time: i64, +} + +impl TryFrom + for ContinuousVestingAccount +{ + type Error = ErrorReport; + + fn try_from( + proto: proto::cosmos::vesting::v1beta1::ContinuousVestingAccount, + ) -> Result { + Ok(ContinuousVestingAccount { + base_vesting_account: proto + .base_vesting_account + .map(TryFrom::try_from) + .transpose()?, + start_time: proto.start_time, + }) + } +} + +impl From for proto::cosmos::vesting::v1beta1::ContinuousVestingAccount { + fn from(account: ContinuousVestingAccount) -> Self { + proto::cosmos::vesting::v1beta1::ContinuousVestingAccount { + base_vesting_account: account.base_vesting_account.map(Into::into), + start_time: 0, + } + } +} diff --git a/cosmrs/src/vesting/delayed_vesting_account.rs b/cosmrs/src/vesting/delayed_vesting_account.rs new file mode 100644 index 00000000..9e2da138 --- /dev/null +++ b/cosmrs/src/vesting/delayed_vesting_account.rs @@ -0,0 +1,35 @@ +use super::BaseVestingAccount; +use crate::{proto, ErrorReport, Result}; + +/// [`DelayedVestingAccount`] implements the `VestingAccount` interface. +/// +/// It vests all coins after a specific time, but non prior. In other words, +/// it keeps them locked until a specified time. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct DelayedVestingAccount { + /// Base vesting account specification required for this vesting implementation. + pub base_vesting_account: Option, +} + +impl TryFrom for DelayedVestingAccount { + type Error = ErrorReport; + + fn try_from( + proto: proto::cosmos::vesting::v1beta1::DelayedVestingAccount, + ) -> Result { + Ok(DelayedVestingAccount { + base_vesting_account: proto + .base_vesting_account + .map(TryFrom::try_from) + .transpose()?, + }) + } +} + +impl From for proto::cosmos::vesting::v1beta1::DelayedVestingAccount { + fn from(account: DelayedVestingAccount) -> Self { + proto::cosmos::vesting::v1beta1::DelayedVestingAccount { + base_vesting_account: account.base_vesting_account.map(Into::into), + } + } +} diff --git a/cosmrs/src/vesting/period.rs b/cosmrs/src/vesting/period.rs new file mode 100644 index 00000000..96414d54 --- /dev/null +++ b/cosmrs/src/vesting/period.rs @@ -0,0 +1,35 @@ +use crate::{proto, Coin, ErrorReport, Result}; + +/// [`Period`] defines a length of time and amount of coins that will vest. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct Period { + /// Length of this vesting period in seconds. + pub length: i64, + + /// The amount of coins (per denomination) that will vest upon this period finishing. + pub amount: Vec, +} + +impl TryFrom for Period { + type Error = ErrorReport; + + fn try_from(proto: proto::cosmos::vesting::v1beta1::Period) -> Result { + Ok(Period { + length: proto.length, + amount: proto + .amount + .into_iter() + .map(TryFrom::try_from) + .collect::>()?, + }) + } +} + +impl From for proto::cosmos::vesting::v1beta1::Period { + fn from(period: Period) -> Self { + proto::cosmos::vesting::v1beta1::Period { + length: period.length, + amount: period.amount.into_iter().map(Into::into).collect(), + } + } +} diff --git a/cosmrs/src/vesting/periodic_vesting_account.rs b/cosmrs/src/vesting/periodic_vesting_account.rs new file mode 100644 index 00000000..af9f779b --- /dev/null +++ b/cosmrs/src/vesting/periodic_vesting_account.rs @@ -0,0 +1,53 @@ +use super::{BaseVestingAccount, Period}; +use crate::{proto, ErrorReport, Result}; + +/// [`PeriodicVestingAccount`] implements the `VestingAccount` interface. +/// +/// It periodically vests by unlocking coins during each specified period. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct PeriodicVestingAccount { + /// Base vesting account specification required for this vesting implementation. + pub base_vesting_account: Option, + + /// The BFT time at which a vesting account starts to vest. + pub start_time: i64, + + /// Vesting [`Period`]s associated with this account. Periods are sequential, + /// in that the duration of a period only starts at the end of the previous period. + pub vesting_periods: Vec, +} + +impl TryFrom for PeriodicVestingAccount { + type Error = ErrorReport; + + fn try_from( + proto: proto::cosmos::vesting::v1beta1::PeriodicVestingAccount, + ) -> Result { + Ok(PeriodicVestingAccount { + base_vesting_account: proto + .base_vesting_account + .map(TryFrom::try_from) + .transpose()?, + start_time: proto.start_time, + vesting_periods: proto + .vesting_periods + .into_iter() + .map(TryFrom::try_from) + .collect::>()?, + }) + } +} + +impl From for proto::cosmos::vesting::v1beta1::PeriodicVestingAccount { + fn from(account: PeriodicVestingAccount) -> Self { + proto::cosmos::vesting::v1beta1::PeriodicVestingAccount { + base_vesting_account: account.base_vesting_account.map(Into::into), + start_time: account.start_time, + vesting_periods: account + .vesting_periods + .into_iter() + .map(Into::into) + .collect(), + } + } +} diff --git a/cosmrs/src/vesting/permanent_locked_account.rs b/cosmrs/src/vesting/permanent_locked_account.rs new file mode 100644 index 00000000..2091e8c7 --- /dev/null +++ b/cosmrs/src/vesting/permanent_locked_account.rs @@ -0,0 +1,36 @@ +use super::BaseVestingAccount; +use crate::{proto, ErrorReport, Result}; + +/// [`PermanentLockedAccount`] implements the `VestingAccount` interface. It does +/// not ever release coins, locking them indefinitely. +/// +/// Coins in this account can still be used for delegating and for governance +/// votes even while locked. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct PermanentLockedAccount { + /// Base vesting account specification required for this vesting implementation. + pub base_vesting_account: Option, +} + +impl TryFrom for PermanentLockedAccount { + type Error = ErrorReport; + + fn try_from( + proto: proto::cosmos::vesting::v1beta1::PermanentLockedAccount, + ) -> Result { + Ok(PermanentLockedAccount { + base_vesting_account: proto + .base_vesting_account + .map(TryFrom::try_from) + .transpose()?, + }) + } +} + +impl From for proto::cosmos::vesting::v1beta1::PermanentLockedAccount { + fn from(account: PermanentLockedAccount) -> Self { + proto::cosmos::vesting::v1beta1::PermanentLockedAccount { + base_vesting_account: account.base_vesting_account.map(Into::into), + } + } +}