diff --git a/src/components/finutils/Cargo.toml b/src/components/finutils/Cargo.toml index d76cef1cd..8d3a84dcc 100644 --- a/src/components/finutils/Cargo.toml +++ b/src/components/finutils/Cargo.toml @@ -20,6 +20,7 @@ rand_chacha = "0.2" curve25519-dalek = { version = "3.0", features = ["serde"] } wasm-bindgen = { version = "=0.2.84", features = ["serde-serialize"] } sha2 = "0.10" +sha3 = "0.8" zei = { git = "https://github.com/FindoraNetwork/zei", branch = "stable-main" } ruc = "1.0" diff --git a/src/components/finutils/src/common/mod.rs b/src/components/finutils/src/common/mod.rs index ab00ab060..a89d92c5b 100644 --- a/src/components/finutils/src/common/mod.rs +++ b/src/components/finutils/src/common/mod.rs @@ -6,6 +6,8 @@ //! This module is the library part of FN. //! +use std::str::FromStr; + #[cfg(not(target_arch = "wasm32"))] pub mod dev; @@ -18,6 +20,7 @@ pub mod utils; use { self::utils::{get_evm_staking_address, get_validator_memo_and_rate}, crate::api::DelegationInfo, + crate::common::utils::mapping_address, globutils::wallet, lazy_static::lazy_static, ledger::{ @@ -291,11 +294,11 @@ pub fn claim(td_addr: &str, am: Option<&str>, sk_str: Option<&str>) -> Result<() pub fn show(basic: bool) -> Result<()> { let kp = get_keypair().c(d!())?; - let serv_addr = ruc::info!(get_serv_addr()).map(|i| { + ruc::info!(get_serv_addr()).map(|i| { println!("\x1b[31;01mServer URL:\x1b[00m\n{i}\n"); - }); + })?; - let xfr_account = ruc::info!(get_keypair()).map(|i| { + ruc::info!(get_keypair()).map(|i| { println!( "\x1b[31;01mFindora Address:\x1b[00m\n{}\n", wallet::public_key_to_bech32(&i.get_pk()) @@ -304,66 +307,42 @@ pub fn show(basic: bool) -> Result<()> { "\x1b[31;01mFindora Public Key:\x1b[00m\n{}\n", wallet::public_key_to_base64(&i.get_pk()) ); - }); + })?; - let self_balance = ruc::info!(utils::get_balance(&kp)).map(|i| { + ruc::info!(utils::get_balance(&kp)).map(|i| { println!("\x1b[31;01mNode Balance:\x1b[00m\n{i} FRA units\n"); - }); + })?; if basic { return Ok(()); } - let td_info = ruc::info!(get_td_pubkey()).map(|i| { + let (_, addr) = ruc::info!(get_td_pubkey()).map(|i| { let addr = td_pubkey_to_td_addr(&i); println!("\x1b[31;01mValidator Node Addr:\x1b[00m\n{addr}\n"); (i, addr) - }); + })?; - let di = utils::get_delegation_info(kp.get_pk_ref()); - let bond_entries = match di.as_ref() { - Ok(di) => Some(di.bond_entries.clone()), - Err(_) => None, - }; + let evm_staking_address = get_evm_staking_address()?; + let url = format!("{}:8545", get_serv_addr()?); + let address = utils::get_trigger_on_contract_address(&url, evm_staking_address)?; + let (bound_amount, unbound_amount) = utils::get_evm_delegation_info( + &url, + address, + H160::from_str(&addr).c(d!())?, + mapping_address(kp.get_pk_ref()), + )?; + + let address = utils::get_claim_on_contract_address(&url, evm_staking_address)?; + let reward = + utils::get_reward_info(&url, address, mapping_address(kp.get_pk_ref()))?; - let delegation_info = di.and_then(|di| { - serde_json::to_string_pretty(&di).c(d!("server returned invalid data")) - }); - let delegation_info = ruc::info!(delegation_info).map(|i| { - println!("\x1b[31;01mYour Delegation:\x1b[00m\n{i}\n"); - }); - - if let Ok((tpk, addr)) = td_info.as_ref() { - let self_delegation = - bond_entries.map_or(false, |bes| bes.iter().any(|i| &i.0 == addr)); - if self_delegation { - let res = utils::get_validator_detail(&td_pubkey_to_td_addr(tpk)) - .c(d!("Validator not found")) - .and_then(|di| { - serde_json::to_string_pretty(&di) - .c(d!("server returned invalid data")) - }) - .map(|i| { - println!("\x1b[31;01mYour Staking:\x1b[00m\n{i}\n"); - }); - ruc::info_omit!(res); - } - } + println!( + "\x1b[31;01mYour Delegation:\x1b[00m\nbound_amount:{:?}\nunbound_amount:{:?}\nreward:{:?}", + bound_amount, unbound_amount,reward + ); - if [ - serv_addr, - xfr_account, - td_info.map(|_| ()), - self_balance, - delegation_info, - ] - .iter() - .any(|i| i.is_err()) - { - Err(eg!("unable to obtain complete information")) - } else { - Ok(()) - } + Ok(()) } /// Setup for a cli tool diff --git a/src/components/finutils/src/common/utils.rs b/src/components/finutils/src/common/utils.rs index 9b0dfc99a..5827142eb 100644 --- a/src/components/finutils/src/common/utils.rs +++ b/src/components/finutils/src/common/utils.rs @@ -23,6 +23,7 @@ use { serde::{self, Deserialize, Serialize}, serde_json::Value, sha2::{Digest, Sha256}, + sha3::Keccak256, std::{ collections::{BTreeMap, HashMap}, str::FromStr, @@ -32,7 +33,7 @@ use { web3::{ ethabi::{Function, Param, ParamType, StateMutability, Token}, transports::Http, - types::{BlockId, BlockNumber, Bytes, CallRequest, H160}, + types::{BlockId, BlockNumber, Bytes, CallRequest, H160, U256}, Web3, }, zei::xfr::{ @@ -745,3 +746,211 @@ pub fn get_validator_memo_and_rate( }; Ok((memo, rate)) } + +#[allow(missing_docs)] +pub fn get_evm_delegation_info( + url: &str, + staking_address: H160, + validator_address: H160, + address: H160, +) -> Result<(U256, U256)> { + let transport = Http::new(url).c(d!())?; + let web3 = Web3::new(transport); + + #[allow(deprecated)] + let function = Function { + name: "delegators".to_owned(), + inputs: vec![ + Param { + name: String::new(), + kind: ParamType::Address, + internal_type: Some(String::from("address")), + }, + Param { + name: String::new(), + kind: ParamType::Address, + internal_type: Some(String::from("address")), + }, + ], + outputs: vec![ + Param { + name: String::from("boundAmount"), + kind: ParamType::Uint(256), + internal_type: Some(String::from("uint256")), + }, + Param { + name: String::from("unboundAmount"), + kind: ParamType::Uint(256), + internal_type: Some(String::from("uint256")), + }, + ], + constant: None, + state_mutability: StateMutability::View, + }; + let data = function + .encode_input(&[Token::Address(validator_address), Token::Address(address)]) + .map_err(|e| eg!("{:?}", e))?; + + let ret_data = Runtime::new() + .c(d!())? + .block_on(web3.eth().call( + CallRequest { + to: Some(staking_address), + data: Some(Bytes(data)), + ..Default::default() + }, + Some(BlockId::Number(BlockNumber::Latest)), + )) + .c(d!())?; + + let ret = function.decode_output(&ret_data.0).c(d!())?; + let bound_amount = if let Some(Token::Uint(bound_amount)) = ret.get(0) { + bound_amount + } else { + return Err(eg!("bound_amount not found")); + }; + let unbound_amount = if let Some(Token::Uint(unbound_amount)) = ret.get(1) { + unbound_amount + } else { + return Err(eg!("unbound_amount not found")); + }; + Ok((*bound_amount, *unbound_amount)) +} + +#[allow(missing_docs)] +pub fn get_reward_info(url: &str, rewards_address: H160, address: H160) -> Result { + let transport = Http::new(url).c(d!())?; + let web3 = Web3::new(transport); + + #[allow(deprecated)] + let function = Function { + name: "rewards".to_owned(), + inputs: vec![Param { + name: String::new(), + kind: ParamType::Address, + internal_type: Some(String::from("address")), + }], + outputs: vec![Param { + name: String::new(), + kind: ParamType::Uint(256), + internal_type: Some(String::from("uint256")), + }], + constant: None, + state_mutability: StateMutability::View, + }; + let data = function + .encode_input(&[Token::Address(address)]) + .map_err(|e| eg!("{:?}", e))?; + + let ret_data = Runtime::new() + .c(d!())? + .block_on(web3.eth().call( + CallRequest { + to: Some(rewards_address), + data: Some(Bytes(data)), + ..Default::default() + }, + Some(BlockId::Number(BlockNumber::Latest)), + )) + .c(d!())?; + + let ret = function.decode_output(&ret_data.0).c(d!())?; + let reward = if let Some(Token::Uint(reward)) = ret.get(0) { + reward + } else { + return Err(eg!("reward not found")); + }; + + Ok(*reward) +} + +#[allow(missing_docs)] +pub fn get_trigger_on_contract_address( + url: &str, + staking_address: H160, +) -> Result { + let transport = Http::new(url).c(d!())?; + let web3 = Web3::new(transport); + + #[allow(deprecated)] + let function = Function { + name: "getTriggerOnContractAddress".to_owned(), + inputs: vec![], + outputs: vec![Param { + name: String::new(), + kind: ParamType::Address, + internal_type: Some(String::from("address")), + }], + constant: None, + state_mutability: StateMutability::View, + }; + let data = function.encode_input(&[]).map_err(|e| eg!("{:?}", e))?; + + let ret_data = Runtime::new() + .c(d!())? + .block_on(web3.eth().call( + CallRequest { + to: Some(staking_address), + data: Some(Bytes(data)), + ..Default::default() + }, + Some(BlockId::Number(BlockNumber::Latest)), + )) + .c(d!())?; + + let ret = function.decode_output(&ret_data.0).c(d!())?; + let address = if let Some(Token::Address(address)) = ret.get(0) { + address + } else { + return Err(eg!("staking address not found")); + }; + + Ok(*address) +} + +#[allow(missing_docs)] +pub fn get_claim_on_contract_address(url: &str, staking_address: H160) -> Result { + let transport = Http::new(url).c(d!())?; + let web3 = Web3::new(transport); + + #[allow(deprecated)] + let function = Function { + name: "getClaimOnContractAddress".to_owned(), + inputs: vec![], + outputs: vec![Param { + name: String::new(), + kind: ParamType::Address, + internal_type: Some(String::from("address")), + }], + constant: None, + state_mutability: StateMutability::View, + }; + let data = function.encode_input(&[]).map_err(|e| eg!("{:?}", e))?; + + let ret_data = Runtime::new() + .c(d!())? + .block_on(web3.eth().call( + CallRequest { + to: Some(staking_address), + data: Some(Bytes(data)), + ..Default::default() + }, + Some(BlockId::Number(BlockNumber::Latest)), + )) + .c(d!())?; + + let ret = function.decode_output(&ret_data.0).c(d!())?; + let address = if let Some(Token::Address(address)) = ret.get(0) { + address + } else { + return Err(eg!("reward address not found")); + }; + + Ok(*address) +} + +#[allow(missing_docs)] +pub fn mapping_address(pk: &XfrPublicKey) -> H160 { + let result = ::digest(pk.as_bytes()); + H160::from_slice(&result.as_slice()[..20]) +}