1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
use komodo_rpc_client::Chain;
use komodo_rpc_client::KomodoRpcApi;

use std::default::Default;

use crate::error::AirdropError;
use crate::error::ErrorKind;

/// Ultimately holds the details of a snapshot, performed on a Komodo (asset)chain.
///
/// The snapshot can be used to perform an Airdrop.

#[derive(Debug, Clone)]
pub struct Snapshot {
    pub chain: Chain,
    pub addresses: Vec<Address>,
    pub amount: f64,
}

pub struct SnapshotBuilder {
    chain: Chain,
    threshold: f64,
    max_addresses: Option<u32>,
    excluded_addresses: Option<Vec<String>>
}

#[derive(Debug, Clone)]
pub struct Address {
    pub addr: String,
    pub amount: f64
}

impl Snapshot {
    pub fn builder() -> SnapshotBuilder {
        Default::default()
    }
}

impl SnapshotBuilder {
    /// Specify the Komodo (asset)chain to take a snapshot from.
    pub fn on_chain(&mut self, chain: Chain) -> &mut Self {
        self.chain = chain;
        self
    }

    /// Set a threshold, such that all addresses contain at least the specified threshold.
    pub fn using_threshold(&mut self, threshold: f64) -> &mut Self {
        self.threshold = threshold;
        self
    }

    /// Include only the top `max` addresses in the snapshot. A max of 10 gives a snapshot of the
    /// top 10 addresses, based on their balance on their chain.
    pub fn max_addresses(&mut self, max: u32) -> &mut Self {
        self.max_addresses = Some(max);
        self
    }

    /// Removes the addresses specified here from the Snapshot, if they exist in the Snapshot.
    pub fn exclude_addresses(&mut self, addresses: Vec<String>) -> &mut Self {
        self.excluded_addresses = Some(addresses);
        self
    }

    /// Builds a Snapshot struct. Here is where the threshold is applied and excluded addresses are removed, if any.
    pub fn build(&self) -> Result<Snapshot, AirdropError> {
        // a lot of code to do a snapshot, using komodod
        let client = match &self.chain {
            Chain::KMD => komodo_rpc_client::Client::new_komodo_client()?,
            _ => komodo_rpc_client::Client::new_assetchain_client(&self.chain)?
        };

        // todo handle any error, after adding error handling
        let mut snapshot = match self.max_addresses {
            Some(max) => client.get_snapshot_max(max),
            None => client.get_snapshot()
        }?;

        if snapshot.addresses.is_empty() {
            return Err(ErrorKind::EmptySnapshot.into())
        }

        if self.threshold > 0.0 {
            snapshot.addresses = snapshot.addresses
                .drain_filter(|saddress| saddress.amount > self.threshold)
                .collect::<Vec<_>>();
        }

        // first, remove any predefined excluded addresses from the snapshotted address vec
        // then, map each address and its corresponding amount to an Address struct.
        let addresses = snapshot.addresses
            .iter()
            .filter(|address| {
                let excluded_addresses = self.excluded_addresses.clone();
                match excluded_addresses  {
                    Some(vec) => !vec.contains(&address.addr),
                    None => return true
                }
            })
            .map(|address| Address { addr: address.addr.clone(), amount: address.amount })
            .collect::<Vec<_>>();

        Ok(Snapshot {
            chain: self.chain.clone(),
            addresses,
            amount: snapshot.total
        })
    }
}

impl Default for SnapshotBuilder {
    fn default() -> Self {
        SnapshotBuilder {
            chain: Chain::KMD,
            threshold: 0.0,
            max_addresses: None,
            excluded_addresses: None
        }
    }
}