avalanche-sdk 0.0.18

Avalanche API/SDK
Documentation
use std::io::{self, Error, ErrorKind};

use crate::p as api_p;
use avalanche_types::{
    avax,
    ids::node,
    key,
    platformvm::{self, add_validator},
    secp256k1fx,
};
use chrono::{DateTime, Utc};
use log::info;

impl<T> crate::wallet::Wallet<T>
where
    T: key::ReadOnly + key::SignOnly + Clone,
{
    /// Adds a node as a validator.
    /// ref. https://github.com/ava-labs/avalanchego/blob/v1.7.9/vms/platformvm/add_validator_tx.go
    /// ref. https://github.com/ava-labs/avalanchego/blob/v1.7.9/vms/platformvm/spend.go
    /// ref. https://github.com/ava-labs/avalanchego/blob/v1.7.9/wallet/chain/p/builder.go
    /// ref. https://github.com/ava-labs/subnet-cli/blob/6bbe9f4aff353b812822af99c08133af35dbc6bd/client/p.go "AddValidator"
    pub async fn add_validator(
        &self,
        node_id: node::Id,
        stake_amount: u64,
        validate_start: DateTime<Utc>,
        validate_end: DateTime<Utc>,
        validate_reward_fee_percent: u32,
    ) -> io::Result<()> {
        let already_validator = self.is_validator(&node_id).await?;
        if already_validator {
            return Err(Error::new(
                ErrorKind::InvalidInput,
                format!("node ID {} is already a validator", node_id),
            ));
        }

        info!("checking balance");
        let cur_balance_p = self.get_balance_p().await?;
        // ref. https://docs.avax.network/learn/platform-overview/transaction-fees/#fee-schedule
        let add_validator_fee = 0_u64;
        if cur_balance_p > stake_amount + add_validator_fee {
            return Err(Error::new(
                ErrorKind::InvalidInput,
                format!("key address {} (balance {} nano-AVAX, network {}) does not have enough to cover stake amount + fee {}", self.p_address, cur_balance_p, self.network_name, stake_amount + add_validator_fee),
             ));
        };

        info!("checking stake inputs and outputs");
        let (ins, unlocked_outs, locked_outs, signers) =
            self.stake(stake_amount, add_validator_fee).await?;
        let mut tx = add_validator::Tx {
            unsigned_tx: avax::BaseTx {
                network_id: self.network_id,
                blockchain_id: self.p_chain_id,
                transferable_outputs: Some(unlocked_outs),
                transferable_inputs: Some(ins),
                ..Default::default()
            },
            validator: platformvm::Validator {
                node_id,
                start: validate_start.timestamp() as u64,
                end: validate_end.timestamp() as u64,
                weight: stake_amount,
            },
            stake_transferable_outputs: Some(locked_outs),
            rewards_owner: secp256k1fx::OutputOwners {
                locktime: 0,
                threshold: 1,
                addrs: vec![self.short_address.clone()],
            },
            shares: validate_reward_fee_percent * 10000,
            ..Default::default()
        };

        /////
        println!();
        println!();
        println!();
        // TODO: prompt for confirmation

        /////
        println!();
        println!();
        println!();
        info!("signing tx");
        tx.sign(signers)?;

        /////
        println!();
        println!();
        println!();
        let b = tx.unsigned_tx.metadata.unwrap().bytes;
        let hb = hex::encode(&b);
        let resp = api_p::issue_tx(&self.http_rpc_endpoint, &hb).await?;
        info!("sent tx {:?}", resp);

        /////
        println!();
        println!();
        println!();
        // TODO: poll to confirm transaction

        /////
        println!();
        println!();
        println!();
        // TODO: check current validators

        /////
        println!();
        println!();
        println!();

        Ok(())
    }
}