use crate::asset::Amount;
use crate::error::{Error, Result};
use crate::operations::OperationId;
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct FillConvertRequest {
pub owner: String,
pub requestid: u32,
pub amount_in: Amount,
pub amount_out: Amount,
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct AuthorReward {
pub author: String,
pub permlink: String,
pub hbd_payout: Amount,
pub hive_payout: Amount,
pub vesting_payout: Amount,
pub curators_vesting_payout: Amount,
pub payout_must_be_claimed: bool,
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct CurationReward {
pub curator: String,
pub reward: Amount,
pub author: String,
pub permlink: String,
pub payout_must_be_claimed: bool,
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct CommentReward {
pub author: String,
pub permlink: String,
pub payout: Amount,
pub author_rewards: i64,
pub total_payout_value: Amount,
pub curator_payout_value: Amount,
pub beneficiary_payout_value: Amount,
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct LiquidityReward {
pub owner: String,
pub payout: Amount,
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct Interest {
pub owner: String,
pub interest: Amount,
pub is_saved_into_hbd_balance: bool,
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct FillVestingWithdraw {
pub from_account: String,
pub to_account: String,
pub withdrawn: Amount,
pub deposited: Amount,
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct FillOrder {
pub current_owner: String,
pub current_orderid: u32,
pub current_pays: Amount,
pub open_owner: String,
pub open_orderid: u32,
pub open_pays: Amount,
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct ShutdownWitness {
pub owner: String,
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct FillTransferFromSavings {
pub from: String,
pub to: String,
pub amount: Amount,
pub request_id: u32,
pub memo: String,
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct Hardfork {
pub hardfork_id: u32,
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct CommentPayoutUpdate {
pub author: String,
pub permlink: String,
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct ReturnVestingDelegation {
pub account: String,
pub vesting_shares: Amount,
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct CommentBenefactorReward {
pub benefactor: String,
pub author: String,
pub permlink: String,
pub hbd_payout: Amount,
pub hive_payout: Amount,
pub vesting_payout: Amount,
pub payout_must_be_claimed: bool,
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct ProducerReward {
pub producer: String,
pub vesting_shares: Amount,
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct ClearNullAccountBalance {
pub total_cleared: Vec<Amount>,
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct ProposalPay {
pub proposal_id: u32,
pub receiver: String,
pub payer: String,
pub payment: Amount,
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct DhfFunding {
pub treasury: String,
pub additional_funds: Amount,
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct HardforkHive {
pub account: String,
pub treasury: String,
pub other_affected_accounts: Vec<String>,
pub hbd_transferred: Amount,
pub hive_transferred: Amount,
pub vests_converted: Amount,
pub total_hive_from_vests: Amount,
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct HardforkHiveRestore {
pub account: String,
pub treasury: String,
pub hbd_transferred: Amount,
pub hive_transferred: Amount,
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct DelayedVoting {
pub voter: String,
pub votes: u64,
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct ConsolidateTreasuryBalance {
pub total_moved: Vec<Amount>,
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct EffectiveCommentVote {
pub voter: String,
pub author: String,
pub permlink: String,
pub weight: u64,
pub rshares: i64,
pub total_vote_weight: u64,
pub pending_payout: Amount,
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct IneffectiveDeleteComment {
pub author: String,
pub permlink: String,
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct DhfConversion {
pub treasury: String,
pub hive_amount_in: Amount,
pub hbd_amount_out: Amount,
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct ExpiredAccountNotification {
pub account: String,
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct ChangedRecoveryAccount {
pub account: String,
pub old_recovery_account: String,
pub new_recovery_account: String,
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct TransferToVestingCompleted {
pub from_account: String,
pub to_account: String,
pub hive_vested: Amount,
pub vesting_shares_received: Amount,
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct PowReward {
pub worker: String,
pub reward: Amount,
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct VestingSharesSplit {
pub owner: String,
pub vesting_shares_before_split: Amount,
pub vesting_shares_after_split: Amount,
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct AccountCreated {
pub new_account_name: String,
pub creator: String,
pub initial_vesting_shares: Amount,
pub initial_delegation: Amount,
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct FillCollateralizedConvertRequest {
pub owner: String,
pub requestid: u32,
pub amount_in: Amount,
pub amount_out: Amount,
pub excess_collateral: Amount,
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct SystemWarning {
pub message: String,
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct FillRecurrentTransfer {
pub from: String,
pub to: String,
pub amount: Amount,
pub memo: String,
pub remaining_executions: u16,
#[serde(default)]
pub extensions: Vec<crate::operations::RecurrentTransferExtension>,
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct FailedRecurrentTransfer {
pub from: String,
pub to: String,
pub amount: Amount,
pub memo: String,
pub consecutive_failures: u8,
pub remaining_executions: u16,
pub deleted: bool,
#[serde(default)]
pub extensions: Vec<crate::operations::RecurrentTransferExtension>,
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct LimitOrderCancelled {
pub seller: String,
pub orderid: u32,
pub amount_back: Amount,
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct ProducerMissed {
pub producer: String,
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct ProposalFee {
pub creator: String,
pub treasury: String,
pub proposal_id: u32,
pub fee: Amount,
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct CollateralizedConvertImmediateConversion {
pub owner: String,
pub requestid: u32,
pub hbd_out: Amount,
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct EscrowApproved {
pub from: String,
pub to: String,
pub agent: String,
pub escrow_id: u32,
pub fee: Amount,
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct EscrowRejected {
pub from: String,
pub to: String,
pub agent: String,
pub escrow_id: u32,
pub hbd_amount: Amount,
pub hive_amount: Amount,
pub fee: Amount,
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct ProxyCleared {
pub account: String,
pub proxy: String,
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct DeclinedVotingRights {
pub account: String,
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize)]
#[serde(untagged)]
#[non_exhaustive]
pub enum VirtualOperation {
FillConvertRequest(FillConvertRequest),
AuthorReward(AuthorReward),
CurationReward(CurationReward),
CommentReward(CommentReward),
LiquidityReward(LiquidityReward),
Interest(Interest),
FillVestingWithdraw(FillVestingWithdraw),
FillOrder(FillOrder),
ShutdownWitness(ShutdownWitness),
FillTransferFromSavings(FillTransferFromSavings),
Hardfork(Hardfork),
CommentPayoutUpdate(CommentPayoutUpdate),
ReturnVestingDelegation(ReturnVestingDelegation),
CommentBenefactorReward(CommentBenefactorReward),
ProducerReward(ProducerReward),
ClearNullAccountBalance(ClearNullAccountBalance),
ProposalPay(ProposalPay),
DhfFunding(DhfFunding),
HardforkHive(HardforkHive),
HardforkHiveRestore(HardforkHiveRestore),
DelayedVoting(DelayedVoting),
ConsolidateTreasuryBalance(ConsolidateTreasuryBalance),
EffectiveCommentVote(EffectiveCommentVote),
IneffectiveDeleteComment(IneffectiveDeleteComment),
DhfConversion(DhfConversion),
ExpiredAccountNotification(ExpiredAccountNotification),
ChangedRecoveryAccount(ChangedRecoveryAccount),
TransferToVestingCompleted(TransferToVestingCompleted),
PowReward(PowReward),
VestingSharesSplit(VestingSharesSplit),
AccountCreated(AccountCreated),
FillCollateralizedConvertRequest(FillCollateralizedConvertRequest),
SystemWarning(SystemWarning),
FillRecurrentTransfer(FillRecurrentTransfer),
FailedRecurrentTransfer(FailedRecurrentTransfer),
LimitOrderCancelled(LimitOrderCancelled),
ProducerMissed(ProducerMissed),
ProposalFee(ProposalFee),
CollateralizedConvertImmediateConversion(CollateralizedConvertImmediateConversion),
EscrowApproved(EscrowApproved),
EscrowRejected(EscrowRejected),
ProxyCleared(ProxyCleared),
DeclinedVotingRights(DeclinedVotingRights),
}
impl VirtualOperation {
pub fn id(&self) -> OperationId {
match self {
VirtualOperation::FillConvertRequest(_) => OperationId::FillConvertRequest,
VirtualOperation::AuthorReward(_) => OperationId::AuthorReward,
VirtualOperation::CurationReward(_) => OperationId::CurationReward,
VirtualOperation::CommentReward(_) => OperationId::CommentReward,
VirtualOperation::LiquidityReward(_) => OperationId::LiquidityReward,
VirtualOperation::Interest(_) => OperationId::Interest,
VirtualOperation::FillVestingWithdraw(_) => OperationId::FillVestingWithdraw,
VirtualOperation::FillOrder(_) => OperationId::FillOrder,
VirtualOperation::ShutdownWitness(_) => OperationId::ShutdownWitness,
VirtualOperation::FillTransferFromSavings(_) => OperationId::FillTransferFromSavings,
VirtualOperation::Hardfork(_) => OperationId::Hardfork,
VirtualOperation::CommentPayoutUpdate(_) => OperationId::CommentPayoutUpdate,
VirtualOperation::ReturnVestingDelegation(_) => OperationId::ReturnVestingDelegation,
VirtualOperation::CommentBenefactorReward(_) => OperationId::CommentBenefactorReward,
VirtualOperation::ProducerReward(_) => OperationId::ProducerReward,
VirtualOperation::ClearNullAccountBalance(_) => OperationId::ClearNullAccountBalance,
VirtualOperation::ProposalPay(_) => OperationId::ProposalPay,
VirtualOperation::DhfFunding(_) => OperationId::DhfFunding,
VirtualOperation::HardforkHive(_) => OperationId::HardforkHive,
VirtualOperation::HardforkHiveRestore(_) => OperationId::HardforkHiveRestore,
VirtualOperation::DelayedVoting(_) => OperationId::DelayedVoting,
VirtualOperation::ConsolidateTreasuryBalance(_) => {
OperationId::ConsolidateTreasuryBalance
}
VirtualOperation::EffectiveCommentVote(_) => OperationId::EffectiveCommentVote,
VirtualOperation::IneffectiveDeleteComment(_) => OperationId::IneffectiveDeleteComment,
VirtualOperation::DhfConversion(_) => OperationId::DhfConversion,
VirtualOperation::ExpiredAccountNotification(_) => {
OperationId::ExpiredAccountNotification
}
VirtualOperation::ChangedRecoveryAccount(_) => OperationId::ChangedRecoveryAccount,
VirtualOperation::TransferToVestingCompleted(_) => {
OperationId::TransferToVestingCompleted
}
VirtualOperation::PowReward(_) => OperationId::PowReward,
VirtualOperation::VestingSharesSplit(_) => OperationId::VestingSharesSplit,
VirtualOperation::AccountCreated(_) => OperationId::AccountCreated,
VirtualOperation::FillCollateralizedConvertRequest(_) => {
OperationId::FillCollateralizedConvertRequest
}
VirtualOperation::SystemWarning(_) => OperationId::SystemWarning,
VirtualOperation::FillRecurrentTransfer(_) => OperationId::FillRecurrentTransfer,
VirtualOperation::FailedRecurrentTransfer(_) => OperationId::FailedRecurrentTransfer,
VirtualOperation::LimitOrderCancelled(_) => OperationId::LimitOrderCancelled,
VirtualOperation::ProducerMissed(_) => OperationId::ProducerMissed,
VirtualOperation::ProposalFee(_) => OperationId::ProposalFee,
VirtualOperation::CollateralizedConvertImmediateConversion(_) => {
OperationId::CollateralizedConvertImmediateConversion
}
VirtualOperation::EscrowApproved(_) => OperationId::EscrowApproved,
VirtualOperation::EscrowRejected(_) => OperationId::EscrowRejected,
VirtualOperation::ProxyCleared(_) => OperationId::ProxyCleared,
VirtualOperation::DeclinedVotingRights(_) => OperationId::DeclinedVotingRights,
}
}
pub fn name(&self) -> &'static str {
self.id().name()
}
pub fn from_json(value: &serde_json::Value) -> Result<Self> {
let (name, payload) = split_operation_json(value)?;
Self::from_parts(&name, payload)
}
fn from_parts(name: &str, value: serde_json::Value) -> Result<Self> {
let name = name.strip_suffix("_operation").unwrap_or(name);
let de_err = |e: serde_json::Error| Error::ser(format!("could not decode {name}: {e}"));
Ok(match name {
"fill_convert_request" => {
VirtualOperation::FillConvertRequest(serde_json::from_value(value).map_err(de_err)?)
}
"author_reward" => {
VirtualOperation::AuthorReward(serde_json::from_value(value).map_err(de_err)?)
}
"curation_reward" => {
VirtualOperation::CurationReward(serde_json::from_value(value).map_err(de_err)?)
}
"comment_reward" => {
VirtualOperation::CommentReward(serde_json::from_value(value).map_err(de_err)?)
}
"liquidity_reward" => {
VirtualOperation::LiquidityReward(serde_json::from_value(value).map_err(de_err)?)
}
"interest" => {
VirtualOperation::Interest(serde_json::from_value(value).map_err(de_err)?)
}
"fill_vesting_withdraw" => VirtualOperation::FillVestingWithdraw(
serde_json::from_value(value).map_err(de_err)?,
),
"fill_order" => {
VirtualOperation::FillOrder(serde_json::from_value(value).map_err(de_err)?)
}
"shutdown_witness" => {
VirtualOperation::ShutdownWitness(serde_json::from_value(value).map_err(de_err)?)
}
"fill_transfer_from_savings" => VirtualOperation::FillTransferFromSavings(
serde_json::from_value(value).map_err(de_err)?,
),
"hardfork" => {
VirtualOperation::Hardfork(serde_json::from_value(value).map_err(de_err)?)
}
"comment_payout_update" => VirtualOperation::CommentPayoutUpdate(
serde_json::from_value(value).map_err(de_err)?,
),
"return_vesting_delegation" => VirtualOperation::ReturnVestingDelegation(
serde_json::from_value(value).map_err(de_err)?,
),
"comment_benefactor_reward" => VirtualOperation::CommentBenefactorReward(
serde_json::from_value(value).map_err(de_err)?,
),
"producer_reward" => {
VirtualOperation::ProducerReward(serde_json::from_value(value).map_err(de_err)?)
}
"clear_null_account_balance" => VirtualOperation::ClearNullAccountBalance(
serde_json::from_value(value).map_err(de_err)?,
),
"proposal_pay" => {
VirtualOperation::ProposalPay(serde_json::from_value(value).map_err(de_err)?)
}
"dhf_funding" => {
VirtualOperation::DhfFunding(serde_json::from_value(value).map_err(de_err)?)
}
"hardfork_hive" => {
VirtualOperation::HardforkHive(serde_json::from_value(value).map_err(de_err)?)
}
"hardfork_hive_restore" => VirtualOperation::HardforkHiveRestore(
serde_json::from_value(value).map_err(de_err)?,
),
"delayed_voting" => {
VirtualOperation::DelayedVoting(serde_json::from_value(value).map_err(de_err)?)
}
"consolidate_treasury_balance" => VirtualOperation::ConsolidateTreasuryBalance(
serde_json::from_value(value).map_err(de_err)?,
),
"effective_comment_vote" => VirtualOperation::EffectiveCommentVote(
serde_json::from_value(value).map_err(de_err)?,
),
"ineffective_delete_comment" => VirtualOperation::IneffectiveDeleteComment(
serde_json::from_value(value).map_err(de_err)?,
),
"dhf_conversion" => {
VirtualOperation::DhfConversion(serde_json::from_value(value).map_err(de_err)?)
}
"expired_account_notification" => VirtualOperation::ExpiredAccountNotification(
serde_json::from_value(value).map_err(de_err)?,
),
"changed_recovery_account" => VirtualOperation::ChangedRecoveryAccount(
serde_json::from_value(value).map_err(de_err)?,
),
"transfer_to_vesting_completed" => VirtualOperation::TransferToVestingCompleted(
serde_json::from_value(value).map_err(de_err)?,
),
"pow_reward" => {
VirtualOperation::PowReward(serde_json::from_value(value).map_err(de_err)?)
}
"vesting_shares_split" => {
VirtualOperation::VestingSharesSplit(serde_json::from_value(value).map_err(de_err)?)
}
"account_created" => {
VirtualOperation::AccountCreated(serde_json::from_value(value).map_err(de_err)?)
}
"fill_collateralized_convert_request" => {
VirtualOperation::FillCollateralizedConvertRequest(
serde_json::from_value(value).map_err(de_err)?,
)
}
"system_warning" => {
VirtualOperation::SystemWarning(serde_json::from_value(value).map_err(de_err)?)
}
"fill_recurrent_transfer" => VirtualOperation::FillRecurrentTransfer(
serde_json::from_value(value).map_err(de_err)?,
),
"failed_recurrent_transfer" => VirtualOperation::FailedRecurrentTransfer(
serde_json::from_value(value).map_err(de_err)?,
),
"limit_order_cancelled" => VirtualOperation::LimitOrderCancelled(
serde_json::from_value(value).map_err(de_err)?,
),
"producer_missed" => {
VirtualOperation::ProducerMissed(serde_json::from_value(value).map_err(de_err)?)
}
"proposal_fee" => {
VirtualOperation::ProposalFee(serde_json::from_value(value).map_err(de_err)?)
}
"collateralized_convert_immediate_conversion" => {
VirtualOperation::CollateralizedConvertImmediateConversion(
serde_json::from_value(value).map_err(de_err)?,
)
}
"escrow_approved" => {
VirtualOperation::EscrowApproved(serde_json::from_value(value).map_err(de_err)?)
}
"escrow_rejected" => {
VirtualOperation::EscrowRejected(serde_json::from_value(value).map_err(de_err)?)
}
"proxy_cleared" => {
VirtualOperation::ProxyCleared(serde_json::from_value(value).map_err(de_err)?)
}
"declined_voting_rights" => VirtualOperation::DeclinedVotingRights(
serde_json::from_value(value).map_err(de_err)?,
),
other => {
return Err(Error::Unknown {
kind: "virtual operation",
name: other.to_string(),
})
}
})
}
}
pub(crate) fn split_operation_json_owned(
value: serde_json::Value,
) -> Result<(String, serde_json::Value)> {
match value {
serde_json::Value::Object(mut map) => {
if map.contains_key("type") && map.contains_key("value") {
let payload = map.remove("value").expect("checked above");
match map.remove("type").expect("checked above") {
serde_json::Value::String(name) => return Ok((name, payload)),
_ => return Err(Error::ser("operation \"type\" is not a string")),
}
}
Err(Error::ser(
"operation must be [name, value] or {type, value}".to_string(),
))
}
serde_json::Value::Array(mut arr) if arr.len() == 2 => {
let payload = arr.pop().expect("length checked");
match arr.pop().expect("length checked") {
serde_json::Value::String(name) => Ok((name, payload)),
_ => Err(Error::ser("operation name is not a string")),
}
}
_ => Err(Error::ser(
"operation must be [name, value] or {type, value}".to_string(),
)),
}
}
pub(crate) fn split_operation_json(
value: &serde_json::Value,
) -> Result<(String, serde_json::Value)> {
if let Some(obj) = value.as_object() {
if let (Some(t), Some(v)) = (obj.get("type"), obj.get("value")) {
let name = t
.as_str()
.ok_or_else(|| Error::ser("operation \"type\" is not a string"))?;
return Ok((name.to_string(), v.clone()));
}
}
if let Some(arr) = value.as_array() {
if arr.len() == 2 {
let name = arr[0]
.as_str()
.ok_or_else(|| Error::ser("operation name is not a string"))?;
return Ok((name.to_string(), arr[1].clone()));
}
}
Err(Error::ser(
"operation must be [name, value] or {type, value}".to_string(),
))
}