diff --git a/.github/workflows/build-release-artifacts.yml b/.github/workflows/build-release-artifacts.yml index b30d4e1803..4bbc2f8f7b 100644 --- a/.github/workflows/build-release-artifacts.yml +++ b/.github/workflows/build-release-artifacts.yml @@ -17,17 +17,6 @@ on: description: Set to build a particular tag type: string -# The key variables also need to be passed to `cross`, which runs in a container and does not -# inherit variables from the parent environment. The `cross` tool is used in the `build` -# job. If any keys are added, the `build-release-artifacts` target in the Justfile must -# also be updated. -env: - GENESIS_PK: ${{ secrets.STABLE_GENESIS_PK }} - GENESIS_SK: ${{ secrets.STABLE_GENESIS_SK }} - FOUNDATION_PK: ${{ secrets.STABLE_FOUNDATION_PK }} - NETWORK_ROYALTIES_PK: ${{ secrets.STABLE_NETWORK_ROYALTIES_PK }} - PAYMENT_FORWARD_PK: ${{ secrets.STABLE_REWARD_FORWARDING_PK }} - jobs: build: name: build diff --git a/Cargo.lock b/Cargo.lock index f74312c858..44e8694dcc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1476,7 +1476,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c" dependencies = [ "memchr", - "regex-automata 0.4.7", + "regex-automata 0.4.8", "serde", ] @@ -3724,8 +3724,8 @@ dependencies = [ "aho-corasick", "bstr", "log", - "regex-automata 0.4.7", - "regex-syntax 0.8.4", + "regex-automata 0.4.8", + "regex-syntax 0.8.5", ] [[package]] @@ -4306,7 +4306,7 @@ dependencies = [ "globset", "log", "memchr", - "regex-automata 0.4.7", + "regex-automata 0.4.8", "same-file", "walkdir", "winapi-util", @@ -5705,6 +5705,7 @@ dependencies = [ "pretty_assertions", "prometheus-parse", "ratatui", + "regex", "reqwest 0.12.7", "serde", "serde_json", @@ -6588,7 +6589,7 @@ dependencies = [ "rand 0.8.5", "rand_chacha 0.3.1", "rand_xorshift 0.3.0", - "regex-syntax 0.8.4", + "regex-syntax 0.8.5", "rusty-fork", "tempfile", "unarray", @@ -7085,14 +7086,14 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.6" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.7", - "regex-syntax 0.8.4", + "regex-automata 0.4.8", + "regex-syntax 0.8.5", ] [[package]] @@ -7106,13 +7107,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.4", + "regex-syntax 0.8.5", ] [[package]] @@ -7123,9 +7124,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" @@ -9897,7 +9898,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] diff --git a/evmlib/src/lib.rs b/evmlib/src/lib.rs index 0093aeac0e..fe712e1b27 100644 --- a/evmlib/src/lib.rs +++ b/evmlib/src/lib.rs @@ -73,8 +73,9 @@ impl CustomNetwork { } } -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)] pub enum Network { + #[default] ArbitrumOne, ArbitrumSepolia, Custom(CustomNetwork), @@ -150,9 +151,3 @@ impl Network { .await } } - -impl Default for Network { - fn default() -> Self { - Self::ArbitrumOne - } -} diff --git a/node-launchpad/Cargo.toml b/node-launchpad/Cargo.toml index 909546c73e..332878595f 100644 --- a/node-launchpad/Cargo.toml +++ b/node-launchpad/Cargo.toml @@ -70,6 +70,7 @@ tui-input = "0.8.0" which = "6.0.1" faccess = "0.2.4" throbber-widgets-tui = "0.7.0" +regex = "1.11.0" [build-dependencies] vergen = { version = "8.2.6", features = ["build", "git", "gitoxide", "cargo"] } diff --git a/node-launchpad/src/components/options.rs b/node-launchpad/src/components/options.rs index 90a626ec33..934578f93e 100644 --- a/node-launchpad/src/components/options.rs +++ b/node-launchpad/src/components/options.rs @@ -192,10 +192,14 @@ impl Component for Options { .style(Style::default().fg(GHOST_WHITE)); // Beta Rewards Program - let beta_legend = " Edit Discord Username "; + let beta_legend = if self.discord_username.is_empty() { + " Add Wallet " + } else { + " Change Wallet " + }; let beta_key = " [Ctrl+B] "; let block2 = Block::default() - .title(" Beta Rewards Program ") + .title(" Wallet ") .title_style(Style::default().bold().fg(GHOST_WHITE)) .style(Style::default().fg(GHOST_WHITE)) .borders(Borders::ALL) @@ -204,7 +208,7 @@ impl Component for Options { vec![Row::new(vec![ Cell::from( Line::from(vec![Span::styled( - " Discord Username: ", + " Wallet Address: ", Style::default().fg(LIGHT_PERIWINKLE), )]) .alignment(Alignment::Left), diff --git a/node-launchpad/src/components/popup/beta_programme.rs b/node-launchpad/src/components/popup/beta_programme.rs index 615c20bcf4..f512f9d0a4 100644 --- a/node-launchpad/src/components/popup/beta_programme.rs +++ b/node-launchpad/src/components/popup/beta_programme.rs @@ -11,27 +11,30 @@ use super::super::Component; use crate::{ action::{Action, OptionsActions}, mode::{InputMode, Scene}, - style::{clear_area, EUCALYPTUS, GHOST_WHITE, INDIGO, LIGHT_PERIWINKLE, VIVID_SKY_BLUE}, + style::{clear_area, EUCALYPTUS, GHOST_WHITE, INDIGO, LIGHT_PERIWINKLE, RED, VIVID_SKY_BLUE}, widgets::hyperlink::Hyperlink, }; use color_eyre::Result; use crossterm::event::{Event, KeyCode, KeyEvent}; use ratatui::{prelude::*, widgets::*}; +use regex::Regex; use tui_input::{backend::crossterm::EventHandler, Input}; -const INPUT_SIZE_USERNAME: u16 = 32; // as per discord docs +const INPUT_SIZE_USERNAME: u16 = 42; // Etherum address plus 0x const INPUT_AREA_USERNAME: u16 = INPUT_SIZE_USERNAME + 2; // +2 for the padding pub struct BetaProgramme { /// Whether the component is active right now, capturing keystrokes + draw things. active: bool, state: BetaProgrammeState, - discord_input_filed: Input, + discord_input_field: Input, // cache the old value incase user presses Esc. old_value: String, back_to: Scene, + can_save: bool, } +#[allow(dead_code)] enum BetaProgrammeState { DiscordIdAlreadySet, ShowTCs, @@ -49,27 +52,43 @@ impl BetaProgramme { Self { active: false, state, - discord_input_filed: Input::default().with_value(username), + discord_input_field: Input::default().with_value(username), old_value: Default::default(), back_to: Scene::Status, + can_save: false, + } + } + + pub fn validate(&mut self) { + if self.discord_input_field.value().is_empty() { + self.can_save = false; + } else { + let re = Regex::new(r"^0x[a-fA-F0-9]{40}$").expect("Failed to compile regex"); + self.can_save = re.is_match(self.discord_input_field.value()); } } fn capture_inputs(&mut self, key: KeyEvent) -> Vec { let send_back = match key.code { KeyCode::Enter => { - let username = self.discord_input_filed.value().to_string().to_lowercase(); - self.discord_input_filed = username.clone().into(); - - debug!( - "Got Enter, saving the discord username {username:?} and switching to DiscordIdAlreadySet, and Home Scene", - ); - self.state = BetaProgrammeState::DiscordIdAlreadySet; - vec![ - Action::StoreDiscordUserName(username.clone()), - Action::OptionsActions(OptionsActions::UpdateBetaProgrammeUsername(username)), - Action::SwitchScene(Scene::Status), - ] + self.validate(); + if self.can_save { + let username = self.discord_input_field.value().to_string().to_lowercase(); + self.discord_input_field = username.clone().into(); + + debug!( + "Got Enter, saving the discord username {username:?} and switching to DiscordIdAlreadySet, and Home Scene", + ); + self.state = BetaProgrammeState::DiscordIdAlreadySet; + return vec![ + Action::StoreDiscordUserName(username.clone()), + Action::OptionsActions(OptionsActions::UpdateBetaProgrammeUsername( + username, + )), // FIXME: Change OptionsActions::UpdateBetaProgrammeUsername name + Action::SwitchScene(Scene::Status), + ]; + } + vec![] } KeyCode::Esc => { debug!( @@ -77,8 +96,8 @@ impl BetaProgramme { self.old_value ); // reset to old value - self.discord_input_filed = self - .discord_input_filed + self.discord_input_field = self + .discord_input_field .clone() .with_value(self.old_value.clone()); vec![Action::SwitchScene(self.back_to)] @@ -86,13 +105,14 @@ impl BetaProgramme { KeyCode::Char(' ') => vec![], KeyCode::Backspace => { // if max limit reached, we should allow Backspace to work. - self.discord_input_filed.handle_event(&Event::Key(key)); + self.discord_input_field.handle_event(&Event::Key(key)); + self.validate(); vec![] } _ => { - // max 32 limit as per discord docs - if self.discord_input_filed.value().chars().count() < 32 { - self.discord_input_filed.handle_event(&Event::Key(key)); + if self.discord_input_field.value().chars().count() < INPUT_SIZE_USERNAME as usize { + self.discord_input_field.handle_event(&Event::Key(key)); + self.validate(); } vec![] } @@ -109,26 +129,27 @@ impl Component for BetaProgramme { // while in entry mode, keybinds are not captured, so gotta exit entry mode from here let send_back = match &self.state { BetaProgrammeState::DiscordIdAlreadySet => self.capture_inputs(key), - BetaProgrammeState::ShowTCs => { - match key.code { - KeyCode::Char('y') | KeyCode::Char('Y') => { - let is_discord_id_set = !self.discord_input_filed.value().is_empty(); - if is_discord_id_set { - debug!("User accepted the TCs, but discord id already set, moving to DiscordIdAlreadySet"); - self.state = BetaProgrammeState::DiscordIdAlreadySet; - } else { - debug!("User accepted the TCs, but no discord id set, moving to AcceptTCsAndEnterDiscordId"); - self.state = BetaProgrammeState::AcceptTCsAndEnterDiscordId; - } + BetaProgrammeState::ShowTCs => match key.code { + KeyCode::Char('y') | KeyCode::Char('Y') => { + let is_discord_id_set = !self.discord_input_field.value().is_empty(); + if is_discord_id_set { + debug!("User accepted the TCs, but discord id already set, moving to DiscordIdAlreadySet"); + self.state = BetaProgrammeState::DiscordIdAlreadySet; + } else { + debug!("User accepted the TCs, but no discord id set, moving to AcceptTCsAndEnterDiscordId"); + self.state = BetaProgrammeState::AcceptTCsAndEnterDiscordId; } - KeyCode::Esc => { - debug!("User rejected the TCs, moving to RejectTCs"); - self.state = BetaProgrammeState::RejectTCs; - } - _ => {} + vec![] } - vec![] - } + KeyCode::Esc => { + debug!("User rejected the TCs, moving to original screen"); + self.state = BetaProgrammeState::ShowTCs; + vec![Action::SwitchScene(self.back_to)] + } + _ => { + vec![] + } + }, BetaProgrammeState::RejectTCs => { if let KeyCode::Esc = key.code { debug!("RejectTCs msg closed. Switching to Status scene."); @@ -146,7 +167,7 @@ impl Component for BetaProgramme { Action::SwitchScene(scene) => match scene { Scene::StatusBetaProgrammePopUp | Scene::OptionsBetaProgrammePopUp => { self.active = true; - self.old_value = self.discord_input_filed.value().to_string(); + self.old_value = self.discord_input_field.value().to_string(); if scene == Scene::StatusBetaProgrammePopUp { self.back_to = Scene::Status; } else if scene == Scene::OptionsBetaProgrammePopUp { @@ -190,7 +211,7 @@ impl Component for BetaProgramme { let pop_up_border = Paragraph::new("").block( Block::default() .borders(Borders::ALL) - .title(" Beta Rewards Program ") + .title(" Add Your Wallet ") .bold() .title_style(Style::new().fg(VIVID_SKY_BLUE)) .padding(Padding::uniform(2)) @@ -200,7 +221,8 @@ impl Component for BetaProgramme { match self.state { BetaProgrammeState::DiscordIdAlreadySet => { - // split into 4 parts, for the prompt, input, text, dash , and buttons + self.validate(); // FIXME: maybe this should be somewhere else + // split into 4 parts, for the prompt, input, text, dash , and buttons let layer_two = Layout::new( Direction::Vertical, [ @@ -218,27 +240,40 @@ impl Component for BetaProgramme { ) .split(layer_one[1]); - let prompt_text = Paragraph::new("Discord Username associated with this device:") - .block(Block::default()) - .alignment(Alignment::Center) - .fg(GHOST_WHITE); + let prompt_text = Paragraph::new(Line::from(vec![ + Span::styled("Enter new ".to_string(), Style::default()), + Span::styled("Wallet Address".to_string(), Style::default().bold()), + ])) + .block(Block::default()) + .alignment(Alignment::Center) + .fg(GHOST_WHITE); f.render_widget(prompt_text, layer_two[0]); let spaces = " ".repeat( - (INPUT_AREA_USERNAME - 1) as usize - self.discord_input_filed.value().len(), + (INPUT_AREA_USERNAME - 1) as usize - self.discord_input_field.value().len(), ); let input = Paragraph::new(Span::styled( - format!("{}{} ", spaces, self.discord_input_filed.value()), - Style::default().fg(VIVID_SKY_BLUE).bg(INDIGO).underlined(), + format!("{}{} ", spaces, self.discord_input_field.value()), + Style::default() + .fg(if self.can_save { VIVID_SKY_BLUE } else { RED }) + .bg(INDIGO) + .underlined(), )) .alignment(Alignment::Center); f.render_widget(input, layer_two[1]); - let text = Paragraph::new(Text::from(vec![ - Line::raw("Changing your Username will reset all nodes,"), - Line::raw("and any Nanos left on this device will be lost."), - ])) + let text = Paragraph::new(Text::from(if self.can_save { + vec![ + Line::raw("Changing your Wallet will reset and restart"), + Line::raw("all your nodes."), + ] + } else { + vec![Line::from(Span::styled( + "Invalid wallet address".to_string(), + Style::default().fg(RED), + ))] + })) .alignment(Alignment::Center) .block( Block::default() @@ -260,15 +295,19 @@ impl Component for BetaProgramme { .split(layer_two[4]); let button_no = Line::from(vec![Span::styled( - " No, Cancel [Esc]", + " Cancel [Esc]", Style::default().fg(LIGHT_PERIWINKLE), )]); f.render_widget(button_no, buttons_layer[0]); let button_yes = Line::from(vec![Span::styled( - "Save Username [Enter]", - Style::default().fg(EUCALYPTUS), + "Change Wallet [Enter]", + if self.can_save { + Style::default().fg(EUCALYPTUS) + } else { + Style::default().fg(LIGHT_PERIWINKLE) + }, )]); f.render_widget(button_yes, buttons_layer[1]); } @@ -290,9 +329,9 @@ impl Component for BetaProgramme { .split(layer_one[1]); let text = Paragraph::new(vec![ - Line::from(Span::styled("Earn a slice of millions of tokens created at the genesis of the Autonomi Network by running nodes to build and test the Beta.",Style::default())), + Line::from(Span::styled("Add your wallet and you can earn a slice of millions of tokens created at the genesis of the Autonomi Network when through running nodes.",Style::default())), Line::from(Span::styled("\n\n",Style::default())), - Line::from(Span::styled("To continue in the beta Rewards Program you agree to the Terms and Conditions found here:",Style::default())), + Line::from(Span::styled("By continuing you agree to the Terms and Conditions found here:",Style::default())), Line::from(Span::styled("\n\n",Style::default())), ] ) @@ -327,10 +366,12 @@ impl Component for BetaProgramme { Style::default().fg(LIGHT_PERIWINKLE), )]); f.render_widget(button_no, buttons_layer[0]); - let button_yes = Line::from(vec![Span::styled( - "Yes, I agree! Continue [Y]", + + let button_yes = Paragraph::new(Line::from(vec![Span::styled( + "Yes, I agree! Continue [Y] ", Style::default().fg(EUCALYPTUS), - )]); + )])) + .alignment(Alignment::Right); f.render_widget(button_yes, buttons_layer[1]); } BetaProgrammeState::RejectTCs => { @@ -381,7 +422,9 @@ impl Component for BetaProgramme { // for the input Constraint::Length(2), // for the text - Constraint::Length(5), + Constraint::Length(3), + // for the hyperlink + Constraint::Length(2), // gap Constraint::Length(1), // for the buttons @@ -390,57 +433,68 @@ impl Component for BetaProgramme { ) .split(layer_one[1]); - let prompt = - Paragraph::new("Enter your Discord Username").alignment(Alignment::Center); + let prompt = Paragraph::new(Line::from(vec![ + Span::styled("Enter your ", Style::default()), + Span::styled("Wallet Address", Style::default().fg(GHOST_WHITE)), + ])) + .alignment(Alignment::Center); f.render_widget(prompt.fg(GHOST_WHITE), layer_two[0]); let spaces = " ".repeat( - (INPUT_AREA_USERNAME - 1) as usize - self.discord_input_filed.value().len(), + (INPUT_AREA_USERNAME - 1) as usize - self.discord_input_field.value().len(), ); let input = Paragraph::new(Span::styled( - format!("{}{} ", spaces, self.discord_input_filed.value()), + format!("{}{} ", spaces, self.discord_input_field.value()), Style::default().fg(VIVID_SKY_BLUE).bg(INDIGO).underlined(), )) .alignment(Alignment::Center); f.render_widget(input, layer_two[1]); - let text = Paragraph::new(vec![ - Line::from(Span::styled( - "Submit your username and track your progress on our Discord server.", - Style::default(), - )), - Line::from(Span::styled("\n\n", Style::default())), - Line::from(Span::styled( - "Note: your username may be different from your display name.", - Style::default(), - )), - ]) + let text = Paragraph::new(vec![Line::from(Span::styled( + "Find out more about compatible wallets, and how to track your earnings:", + Style::default(), + ))]) .block(Block::default().padding(Padding::horizontal(2))) .wrap(Wrap { trim: false }); f.render_widget(text.fg(GHOST_WHITE), layer_two[2]); + let link = Hyperlink::new( + Span::styled( + " https://autonomi.com/wallet", + Style::default().fg(VIVID_SKY_BLUE), + ), + "https://autonomi.com/wallet", + ); + + f.render_widget_ref(link, layer_two[3]); + let dash = Block::new() .borders(Borders::BOTTOM) .border_style(Style::new().fg(GHOST_WHITE)); - f.render_widget(dash, layer_two[3]); + f.render_widget(dash, layer_two[4]); let buttons_layer = Layout::horizontal(vec![ Constraint::Percentage(50), Constraint::Percentage(50), ]) - .split(layer_two[4]); + .split(layer_two[5]); let button_no = Line::from(vec![Span::styled( - " No, Cancel [Esc]", + " Cancel [Esc]", Style::default().fg(LIGHT_PERIWINKLE), )]); f.render_widget(button_no, buttons_layer[0]); - let button_yes = Line::from(vec![Span::styled( - "Submit Username [Enter]", - Style::default().fg(EUCALYPTUS), - )]); + let button_yes = Paragraph::new(Line::from(vec![Span::styled( + "Save Wallet [Enter] ", + if self.can_save { + Style::default().fg(EUCALYPTUS) + } else { + Style::default().fg(LIGHT_PERIWINKLE) + }, + )])) + .alignment(Alignment::Right); f.render_widget(button_yes, buttons_layer[1]); } } diff --git a/node-launchpad/src/components/status.rs b/node-launchpad/src/components/status.rs index 69dd9a4d90..90706c488d 100644 --- a/node-launchpad/src/components/status.rs +++ b/node-launchpad/src/components/status.rs @@ -59,7 +59,7 @@ const MAX_ERRORS_WHILE_RUNNING_NAT_DETECTION: usize = 3; // Table Widths const NODE_WIDTH: usize = 10; const VERSION_WIDTH: usize = 7; -const NANOS_WIDTH: usize = 5; +const ATTOS_WIDTH: usize = 5; const MEMORY_WIDTH: usize = 7; const MBPS_WIDTH: usize = 15; const RECORDS_WIDTH: usize = 4; @@ -207,7 +207,7 @@ impl Status<'_> { .iter() .find(|s| s.service_name == node_item.service_name) { - item.nanos = stats.forwarded_rewards; + item.attos = stats.forwarded_rewards; item.memory = stats.memory_usage_mb; item.mbps = format!( "↓{:06.2} ↑{:06.2}", @@ -222,7 +222,7 @@ impl Status<'_> { let new_item = NodeItem { name: node_item.service_name.clone(), version: node_item.version.to_string(), - nanos: 0, + attos: 0, memory: 0, mbps: "-".to_string(), records: 0, @@ -256,7 +256,7 @@ impl Status<'_> { Some(NodeItem { name: node_item.service_name.clone().to_string(), version: node_item.version.to_string(), - nanos: 0, + attos: 0, memory: 0, mbps: "-".to_string(), records: 0, @@ -539,6 +539,7 @@ impl Component for Status<'_> { action_sender: action_sender.clone(), connection_mode: self.connection_mode, port_range: Some(port_range), + rewards_address: self.discord_username.clone(), }; debug!("Calling maintain_n_running_nodes"); @@ -561,7 +562,11 @@ impl Component for Status<'_> { stop_nodes(running_nodes, action_sender); } StatusActions::TriggerBetaProgramme => { - return Ok(Some(Action::SwitchScene(Scene::StatusBetaProgrammePopUp))); + if self.discord_username.is_empty() { + return Ok(Some(Action::SwitchScene(Scene::StatusBetaProgrammePopUp))); + } else { + return Ok(None); + } } }, Action::OptionsActions(OptionsActions::ResetNodes) => { @@ -661,54 +666,44 @@ impl Component for Status<'_> { let column_constraints = [Constraint::Length(23), Constraint::Fill(1)]; let stats_table = Table::new(stats_rows, stats_width).widths(column_constraints); - // Combine "Nanos Earned" and "Username" into a single row - let discord_username_placeholder = "Username: "; // Used to calculate the width of the username column - let discord_username_no_username = "[Ctrl+B] to set"; - let discord_username_title = Span::styled( - discord_username_placeholder, - Style::default().fg(VIVID_SKY_BLUE), - ); - - let discord_username = if !self.discord_username.is_empty() { - Span::styled( - self.discord_username.clone(), - Style::default().fg(VIVID_SKY_BLUE), - ) - .bold() + let wallet_not_set = if self.discord_username.is_empty() { + vec![ + Span::styled("Press ".to_string(), Style::default().fg(VIVID_SKY_BLUE)), + Span::styled("[Ctrl+B] ".to_string(), Style::default().fg(GHOST_WHITE)), + Span::styled( + "to add your ".to_string(), + Style::default().fg(VIVID_SKY_BLUE), + ), + Span::styled( + "Wallet Address".to_string(), + Style::default().fg(VIVID_SKY_BLUE).bold(), + ), + ] } else { - Span::styled( - discord_username_no_username, - Style::default().fg(GHOST_WHITE), - ) + vec![] }; - let total_nanos_earned_and_discord_row = Row::new(vec![ - Cell::new("Nanos Earned".to_string()).fg(VIVID_SKY_BLUE), + let total_attos_earned_and_wallet_row = Row::new(vec![ + Cell::new("Attos Earned".to_string()).fg(VIVID_SKY_BLUE), Cell::new(self.node_stats.total_forwarded_rewards.to_string()) .fg(VIVID_SKY_BLUE) .bold(), - Cell::new( - Line::from(vec![discord_username_title, discord_username]) - .alignment(Alignment::Right), - ), + Cell::new(Line::from(wallet_not_set).alignment(Alignment::Right)), ]); - let nanos_discord_rows = vec![total_nanos_earned_and_discord_row]; - let nanos_discord_width = [Constraint::Length(5)]; + let attos_wallet_rows = vec![total_attos_earned_and_wallet_row]; + let attos_wallet_width = [Constraint::Length(5)]; let column_constraints = [ Constraint::Length(23), Constraint::Fill(1), - Constraint::Length( - discord_username_placeholder.len() as u16 - + if !self.discord_username.is_empty() { - self.discord_username.len() as u16 - } else { - discord_username_no_username.len() as u16 - }, - ), + Constraint::Length(if self.discord_username.is_empty() { + 41 //TODO: make it dynamic with wallet_not_set + } else { + 0 + }), ]; - let nanos_discord_table = - Table::new(nanos_discord_rows, nanos_discord_width).widths(column_constraints); + let attos_wallet_table = + Table::new(attos_wallet_rows, attos_wallet_width).widths(column_constraints); let inner_area = combined_block.inner(layout[1]); let device_layout = Layout::new( @@ -719,7 +714,7 @@ impl Component for Status<'_> { // Render both tables inside the combined block f.render_widget(stats_table, device_layout[0]); - f.render_widget(nanos_discord_table, device_layout[1]); + f.render_widget(attos_wallet_table, device_layout[1]); // ==== Node Status ===== @@ -783,7 +778,7 @@ impl Component for Status<'_> { let node_widths = [ Constraint::Min(NODE_WIDTH as u16), Constraint::Min(VERSION_WIDTH as u16), - Constraint::Min(NANOS_WIDTH as u16), + Constraint::Min(ATTOS_WIDTH as u16), Constraint::Min(MEMORY_WIDTH as u16), Constraint::Min(MBPS_WIDTH as u16), Constraint::Min(RECORDS_WIDTH as u16), @@ -797,7 +792,7 @@ impl Component for Status<'_> { let header_row = Row::new(vec![ Cell::new("Node").fg(COOL_GREY), Cell::new("Version").fg(COOL_GREY), - Cell::new("Nanos").fg(COOL_GREY), + Cell::new("Attos").fg(COOL_GREY), Cell::new("Memory").fg(COOL_GREY), Cell::new( format!("{}{}", " ".repeat(MBPS_WIDTH - "Mbps".len()), "Mbps") @@ -1022,7 +1017,7 @@ impl fmt::Display for NodeStatus { pub struct NodeItem<'a> { name: String, version: String, - nanos: u64, + attos: u64, memory: usize, mbps: String, records: usize, @@ -1075,8 +1070,8 @@ impl NodeItem<'_> { self.version.to_string(), format!( "{}{}", - " ".repeat(NANOS_WIDTH.saturating_sub(self.nanos.to_string().len())), - self.nanos.to_string() + " ".repeat(ATTOS_WIDTH.saturating_sub(self.attos.to_string().len())), + self.attos.to_string() ), format!( "{}{} MB", diff --git a/node-launchpad/src/node_mgmt.rs b/node-launchpad/src/node_mgmt.rs index 852e8da8a7..2c3b6205a9 100644 --- a/node-launchpad/src/node_mgmt.rs +++ b/node-launchpad/src/node_mgmt.rs @@ -1,7 +1,7 @@ use crate::action::{Action, StatusActions}; use crate::connection_mode::ConnectionMode; use color_eyre::eyre::{eyre, Error}; -use sn_evm::RewardsAddress; +use sn_evm::{EvmNetwork, RewardsAddress}; use sn_node_manager::{ add_services::config::PortRange, config::get_node_registry_path, VerbosityLevel, }; @@ -51,6 +51,7 @@ pub struct MaintainNodesArgs { pub action_sender: UnboundedSender, pub connection_mode: ConnectionMode, pub port_range: Option, + pub rewards_address: String, } /// Maintain the specified number of nodes @@ -172,6 +173,7 @@ struct NodeConfig { data_dir_path: Option, peers_args: PeersArgs, safenode_path: Option, + rewards_address: String, } /// Run the NAT detection process @@ -234,6 +236,7 @@ fn prepare_node_config(args: &MaintainNodesArgs) -> NodeConfig { data_dir_path: args.data_dir_path.clone(), peers_args: args.peers_args.clone(), safenode_path: args.safenode_path.clone(), + rewards_address: args.rewards_address.clone(), } } @@ -288,6 +291,7 @@ async fn scale_down_nodes(config: &NodeConfig, count: u16) { config.data_dir_path.clone(), true, None, + Some(EvmNetwork::ArbitrumSepolia), //FIXME: should come from an UI element. config.home_network, false, None, @@ -299,7 +303,7 @@ async fn scale_down_nodes(config: &NodeConfig, count: u16) { None, // We don't care about the port, as we are scaling down config.owner.clone(), config.peers_args.clone(), - RewardsAddress::from_str("0x1111111111111111111111111111111111111111").unwrap(), + RewardsAddress::from_str(config.rewards_address.as_str()).unwrap(), None, None, config.safenode_path.clone(), @@ -362,6 +366,7 @@ async fn add_nodes( config.data_dir_path.clone(), true, None, + Some(EvmNetwork::ArbitrumSepolia), //FIXME: Should come from an UI element config.home_network, false, None, @@ -373,7 +378,7 @@ async fn add_nodes( port_range, config.owner.clone(), config.peers_args.clone(), - RewardsAddress::from_str("0x1111111111111111111111111111111111111111").unwrap(), + RewardsAddress::from_str(config.rewards_address.as_str()).unwrap(), None, None, config.safenode_path.clone(), diff --git a/sn_node_manager/src/cmd/node.rs b/sn_node_manager/src/cmd/node.rs index d28dbf7266..7d6a10871a 100644 --- a/sn_node_manager/src/cmd/node.rs +++ b/sn_node_manager/src/cmd/node.rs @@ -610,6 +610,7 @@ pub async fn maintain_n_running_nodes( data_dir_path: Option, enable_metrics_server: bool, env_variables: Option>, + evm_network: Option, home_network: bool, local: bool, log_dir_path: Option, @@ -714,7 +715,7 @@ pub async fn maintain_n_running_nodes( data_dir_path.clone(), enable_metrics_server, env_variables.clone(), - None, + evm_network.clone(), home_network, local, log_dir_path.clone(), diff --git a/sn_service_management/src/node.rs b/sn_service_management/src/node.rs index b2e4af4eaa..c9d853a009 100644 --- a/sn_service_management/src/node.rs +++ b/sn_service_management/src/node.rs @@ -283,6 +283,7 @@ pub struct NodeServiceData { )] pub connected_peers: Option>, pub data_dir_path: PathBuf, + #[serde(default)] pub evm_network: EvmNetwork, pub genesis: bool, pub home_network: bool, @@ -307,6 +308,7 @@ pub struct NodeServiceData { )] pub peer_id: Option, pub pid: Option, + #[serde(default)] pub rewards_address: RewardsAddress, pub reward_balance: Option, pub rpc_socket_addr: SocketAddr,