golem-rpc-api 0.2.0

Typesafe binding for Brass Golem RPC services
Documentation
use crate::rpc::*;
use crate::serde::{opt_ts_seconds, ts_seconds};
use bigdecimal::BigDecimal;
use failure::_core::str::FromStr;
use serde::*;
use std::vec::Vec;

rpc_interface! {
    trait GolemPay {
        #[rpc_uri = "pay.operations"]
        fn get_operations(&self, operation_type: Option<WalletOperationType>, direction: Option<WalletOperationDirection>, page: usize, per_page: usize) -> Result<(u32, Vec<WalletOperation>)>;

        #[deprecated(since="0.2.2", note="please use `get_operations` instead")]
        #[rpc_uri = "pay.incomes"]
        fn get_incomes_list(&self) -> Result<Vec<Income>>;

        #[deprecated(since="0.2.2", note="please use `get_operations` instead")]
        #[rpc_uri = "pay.payments"]
        fn get_payments_list(&self, #[kwarg] _num: Option<u32>, #[kwarg] _last_seconds: Option<u32>) -> Result<Vec<Payment>>;

        // TODO: kwargs limit=1000, offset=0
        #[deprecated(since="0.2.2", note="please use `get_operations` instead")]
        #[rpc_uri = "pay.deposit_payments"]
        fn get_deposit_payments_list(&self, #[kwarg] _limit : Option<usize>, #[kwarg] _offset : Option<usize>) -> Result<Option<Vec<DepositPayment>>>;

        #[rpc_uri = "pay.deposit_balance"]
        fn get_deposit_balance(&self) -> Result<Option<DepositBalance>>;

        #[rpc_uri = "pay.balance"]
        fn get_pay_balance(&self) -> Result<Balance>;

        #[rpc_uri = "pay.ident"]
        fn get_pay_ident(&self) -> Result<String>;

        #[rpc_uri = "pay.withdraw"]
        fn withdraw(&self, amount : String, destination : String, currency : String, gas_price : Option<String>) -> Result<Vec<String>>;
    }

    converter AsGolemPay as_golem_pay;
}

/// The status of a payment.
#[derive(Serialize, Deserialize, Debug, Clone, Copy, Ord, PartialOrd, PartialEq, Eq)]
#[repr(u8)]
#[serde(rename_all = "lowercase")]
pub enum PaymentStatus {
    /// Created but not introduced to the payment network.
    Awaiting = 1,

    /// Sent to the payment network.
    Sent = 2,

    /// Confirmed on the payment network.
    Confirmed = 3,

    /// Not confirmed on the payment network, but expected to be.
    Overdue = 4,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Income {
    pub subtask: String,
    pub payer: String,
    pub value: BigDecimal,
    // status
    pub status: PaymentStatus,
    pub transaction: Option<String>,
    #[serde(with = "ts_seconds")]
    pub created: chrono::DateTime<chrono::Utc>,
    #[serde(with = "ts_seconds")]
    pub modified: chrono::DateTime<chrono::Utc>,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Payment {
    pub value: BigDecimal,
    pub fee: Option<BigDecimal>,
    pub subtask: String,
    pub payee: String,
    pub status: PaymentStatus,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct DepositPayment {
    pub value: BigDecimal,
    pub status: PaymentStatus,
    pub fee: Option<BigDecimal>,
    pub transaction: String,
    pub created: chrono::DateTime<chrono::Utc>,
    pub modified: Option<chrono::DateTime<chrono::Utc>>,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "lowercase")]
pub enum DepositStatus {
    Locked,
    Unlocking,
    Unlocked,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct DepositBalance {
    pub status: DepositStatus,
    pub timelock: BigDecimal,
    #[serde(rename = "value")]
    pub balance: BigDecimal,
}

#[derive(Deserialize, Serialize)]
pub struct Balance {
    #[serde(default)]
    pub eth: BigDecimal,
    #[serde(default)]
    pub eth_lock: BigDecimal,
    #[serde(default)]
    pub av_gnt: BigDecimal,
    #[serde(default)]
    pub gnt_lock: BigDecimal,
    #[serde(default)]
    pub gnt_nonconverted: BigDecimal,
}

#[derive(Serialize, Deserialize)]
pub struct TaskPayment {
    pub node: crate::net::NodeInfo,
    pub task_id: String,
    pub subtask_id: String,
    pub charged_from_deposit: Option<bool>,
    #[serde(with = "opt_ts_seconds")]
    pub accepted_ts: Option<chrono::DateTime<chrono::Utc>>,
    #[serde(with = "opt_ts_seconds")]
    pub settled_ts: Option<chrono::DateTime<chrono::Utc>>,
    pub missing_amount: BigDecimal,
    #[serde(with = "ts_seconds")]
    pub created: chrono::DateTime<chrono::Utc>,
    #[serde(with = "ts_seconds")]
    pub modified: chrono::DateTime<chrono::Utc>,
}

#[derive(Deserialize, Serialize, Debug, Clone)]
#[serde(rename_all = "snake_case")]
pub enum WalletOperationDirection {
    Incoming,
    Outgoing,
}

impl FromStr for WalletOperationDirection {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "incoming" => Ok(Self::Incoming),
            "outgoing" => Ok(Self::Outgoing),
            _ => Err(format!("invalid value {}", s)),
        }
    }
}

impl WalletOperationDirection {
    pub fn variants() -> &'static [&'static str] {
        &["incoming", "outgoing"]
    }
}

//arg_enum! {
#[derive(Deserialize, Serialize, Debug, Clone)]
#[serde(rename_all = "snake_case")]
pub enum WalletOperationType {
    Transfer,
    DepositTransfer,
    TaskPayment,
    DepositPayment,
}
//}

impl FromStr for WalletOperationType {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        Ok(match s {
            "transfer" => Self::Transfer,
            "deposit_transfer" => Self::DepositTransfer,
            "task_payment" => Self::TaskPayment,
            "deposit_payment" => Self::DepositPayment,
            _ => return Err(format!("invalid value {}", s)),
        })
    }
}

impl WalletOperationType {
    pub fn variants() -> &'static [&'static str] {
        &[
            "transfer",
            "deposit_transfer",
            "task_payment",
            "deposit_payment",
        ]
    }
}

#[derive(Deserialize, Serialize, Debug)]
pub enum WalletOperationCurrency {
    ETH,
    GNT,
}

#[derive(Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum WalletOperationStatus {
    Awaiting,
    Sent,
    Confirmed,
    Overdue,
    Failed,
    ArbitragedByConcent,
}

#[derive(Serialize, Deserialize)]
pub struct WalletOperation {
    pub task_payment: Option<TaskPayment>,
    pub transaction_hash: Option<String>,
    pub direction: WalletOperationDirection,
    pub operation_type: WalletOperationType,
    pub status: WalletOperationStatus,
    pub sender_address: String,
    pub recipient_address: String,
    pub amount: BigDecimal,
    pub currency: WalletOperationCurrency,
    pub gas_cost: Option<BigDecimal>,
    #[serde(with = "ts_seconds")]
    pub created: chrono::DateTime<chrono::Utc>,
    #[serde(with = "ts_seconds")]
    pub modified: chrono::DateTime<chrono::Utc>,
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_parse_income() {
        let str = r#"{
            "subtask": "1e12a7e4-50a3-11e9-9521-1bac4bb5328e",
            "payer": "1bac4bb5328ec9fc70b336e7c720dbb0c9cd23b8782c8bfc66f2a946ab036b8b6b71775cb04ad08d606f652ba476d9692c77cbd135f534d68f54c12e3edc039a",
            "value": "33333333333333334",
            "status": "awaiting",
            "transaction": null,
            "created": 1553699819.286437,
            "modified": 1557242132.961853
        }"#;

        let income: Income = serde_json::from_str(str).unwrap();

        eprintln!("income = {:?}", income);
    }

    #[test]
    fn test_parse_wallet() {
        let str = r#"[
            13045, [
                {
                "task_payment": {
                    "node": {"node_name": "R03 Laughing Octopus", "key": "b7da70a8bbb439e8f1cdf7494ce163294a884ccaf9f117023e3cad5e5d4cb60655aec697c859fdeb9880ef94e3705ee0a2dd44eba7d68cabcd90e7972b48d00f", "prv_port": 40201, "pub_port": 40201, "p2p_prv_port": 40200, "p2p_pub_port": 40200, "prv_addr": "10.30.8.60", "pub_addr": "5.226.70.4", "prv_addresses": ["10.30.8.60", "172.16.136.1", "172.16.80.1", "172.17.0.1"], "hyperdrive_prv_port": 3282, "hyperdrive_pub_port": 3282, "port_statuses": {"40200": "timeout", "40201": "timeout", "3282": "timeout"}, "nat_type": []}, 
                    "task_id": "", 
                    "subtask_id": "02b2e4de-4184-11e8-8132-b7da70a8bbb4", 
                    "charged_from_deposit": null, 
                    "accepted_ts": 1535984463, 
                    "settled_ts": null, 
                    "missing_amount": "0", 
                    "created": 1540406987.388191, 
                    "modified": 1573816279
                    }, 
                "transaction_hash": "0x2a2dc7ac2044fd33d00131d9b4171e3ca8768c7f8f16c9de23d6f4329c1aec05", 
                "direction": "incoming", 
                "operation_type": "task_payment", 
                "status": "confirmed", 
                "sender_address": "", 
                "recipient_address": "0x0x5C49Ed170D0860273b39CCa561758523148d2bAe", 
                "amount": "790277777777777778", 
                "currency": "GNT", 
                "gas_cost": "0", 
                "created": 1540406987.388191, 
                "modified": 1573816279}
                ]
            ]"#;
        let result: (i32, Vec<WalletOperation>) = serde_json::from_str(str).unwrap();
    }

    #[test]
    fn test_variants() {
        for v in WalletOperationType::variants() {
            let _: WalletOperationType = v.parse().unwrap();
        }
    }
}