diff --git a/seedelf-platform/seedelf-cli/src/commands/fund.rs b/seedelf-platform/seedelf-cli/src/commands/fund.rs index 9edd2d5..d95dc75 100644 --- a/seedelf-platform/seedelf-cli/src/commands/fund.rs +++ b/seedelf-platform/seedelf-cli/src/commands/fund.rs @@ -17,6 +17,14 @@ use seedelf_core::utxos; use seedelf_crypto::register::Register; use seedelf_display::{display, text_coloring}; use seedelf_koios::koios::{UtxoResponse, extract_bytes_with_logging}; +use serde::Serialize; + +#[derive(Serialize)] +pub struct FundSeedelfOutput { + pub tx_cbor: String, + pub tx_fee: u64, + pub usable_utxos: Vec, +} /// Struct to hold command-specific arguments #[derive(Args)] @@ -130,6 +138,51 @@ pub async fn run(args: FundArgs, network_flag: bool, variant: u64) -> Result<()> bail!("Supplied Address Is Incorrect"); } + let FundSeedelfOutput { + tx_cbor, + tx_fee, + usable_utxos, + } = build_fund_seedelf( + config, + network_flag, + args.address, + args.seedelf, + args.lovelace.unwrap_or(minimum_lovelace), + selected_tokens, + ) + .await; + + if usable_utxos.is_empty() { + bail!("Not Enough Lovelace/Tokens"); + } + + println!( + "{} {}", + "\nTx Size Fee:".bright_blue(), + tx_fee.to_string().bright_white() + ); + + println!("\nTx Cbor: {}", tx_cbor.clone().white()); + + // inject the tx cbor into the local webserver to prompt the wallet + display::webserver_address(); + web_server::run_web_server(tx_cbor, network_flag).await; + text_coloring::display_purple("Server has stopped."); + + Ok(()) +} + +pub async fn build_fund_seedelf( + config: Config, + network_flag: bool, + user_address: String, + seedelf: String, + lovelace: u64, + selected_tokens: Assets, +) -> FundSeedelfOutput { + // we need to make sure that the network flag and the address provided makes sense here + let addr: Address = Address::from_bech32(user_address.as_str()).unwrap(); + // we need this as the address type and not the shelley let wallet_addr: Address = address::wallet_contract(network_flag, config.contract.wallet_contract_hash); @@ -137,34 +190,39 @@ pub async fn run(args: FundArgs, network_flag: bool, variant: u64) -> Result<()> // this is used to calculate the real fee let mut draft_tx: StagingTransaction = StagingTransaction::new(); - // we need about 2 ada for change so just add that to the amount - let lovelace: u64 = args.lovelace.unwrap_or(minimum_lovelace); - let lovelace_goal: u64 = lovelace; + let every_utxo_at_script: Vec = + utxos::get_credential_utxos(config.contract.wallet_contract_hash, network_flag) + .await + .unwrap_or_default(); - // utxos - let every_utxo: Vec = - utxos::get_credential_utxos(config.contract.wallet_contract_hash, network_flag).await?; - let seedelf_utxo: UtxoResponse = utxos::find_seedelf_utxo( - args.seedelf.clone(), + let seedelf_utxo: UtxoResponse = match utxos::find_seedelf_utxo( + seedelf.clone(), &config.contract.seedelf_policy_id, - every_utxo, - )? - .ok_or("Seedelf Not Found".to_string()) - .unwrap(); + every_utxo_at_script, + ) { + Ok(Some(utxo)) => utxo, + _ => UtxoResponse::default(), + }; + let seedelf_datum: Register = extract_bytes_with_logging(&seedelf_utxo.inline_datum) .ok_or("Not Register Type".to_string()) - .unwrap(); - - let every_utxo: Vec = - utxos::get_address_utxos(&args.address, network_flag).await?; - let all_utxos: Vec = utxos::collect_address_utxos(every_utxo)?; + .unwrap_or_default(); + + let every_utxo_at_address: Vec = + utxos::get_address_utxos(&user_address, network_flag) + .await + .unwrap_or_default(); + // all non collateral utxos, assume 5 ada for collateral + let every_non_collatreal_utxo: Vec = + utxos::collect_address_utxos(every_utxo_at_address).unwrap_or_default(); let usable_utxos: Vec = - utxos::select(all_utxos, lovelace_goal, selected_tokens.clone())?; + utxos::select(every_non_collatreal_utxo, lovelace, selected_tokens.clone()) + .unwrap_or_default(); - if usable_utxos.is_empty() { - bail!("Not Enough Lovelace/Tokens"); - } + let (total_lovelace, tokens) = utxos::assets_of(usable_utxos.clone()).unwrap_or_default(); + let change_tokens: Assets = tokens.separate(selected_tokens.clone()).unwrap_or_default(); + // add usable wallet utxos as inputs for utxo in usable_utxos.clone() { // draft and raw are built the same here draft_tx = draft_tx.input(Input::new( @@ -178,18 +236,14 @@ pub async fn run(args: FundArgs, network_flag: bool, variant: u64) -> Result<()> )); } - let (total_lovelace, tokens) = utxos::assets_of(usable_utxos)?; - // tokens tha need to be put into the change output - let change_tokens: Assets = tokens.separate(selected_tokens.clone())?; - // if the seedelf isn't found then error - if total_lovelace < lovelace_goal { - bail!("Not Enough Lovelace/Tokens"); - } - // This is some semi legit fee to be used to estimate it let tmp_fee: u64 = 200_000; - let datum_vector: Vec = seedelf_datum.rerandomize()?.to_vec()?; + let datum_vector: Vec = seedelf_datum + .rerandomize() + .unwrap_or_default() + .to_vec() + .unwrap_or_default(); let mut fund_output: Output = Output::new(wallet_addr.clone(), lovelace).set_inline_datum(datum_vector.clone()); for asset in selected_tokens.items.clone() { @@ -208,7 +262,7 @@ pub async fn run(args: FundArgs, network_flag: bool, variant: u64) -> Result<()> let mut number_of_change_utxo: usize = change_token_per_utxo.len(); let mut lovelace_amount: u64 = total_lovelace; for (i, change) in change_token_per_utxo.iter().enumerate() { - let minimum: u64 = wallet_minimum_lovelace_with_assets(change.clone())?; + let minimum: u64 = wallet_minimum_lovelace_with_assets(change.clone()).unwrap_or_default(); let change_lovelace: u64 = if i == number_of_change_utxo - 1 { // this is the last one or the only one lovelace_amount = lovelace_amount - lovelace - tmp_fee; @@ -241,6 +295,7 @@ pub async fn run(args: FundArgs, network_flag: bool, variant: u64) -> Result<()> for i in 0..number_of_change_utxo { raw_tx = raw_tx.remove_output(number_of_change_utxo - i); } + // let mut raw_tx: StagingTransaction = draft_tx.clone().remove_output(1).clear_fee(); // build an intermediate tx for fee estimation let intermediate_tx: BuiltTransaction = draft_tx.build_conway_raw().unwrap(); @@ -261,11 +316,6 @@ pub async fn run(args: FundArgs, network_flag: bool, variant: u64) -> Result<()> // floor division means its safer to just add 1 lovelace let tx_fee: u64 = fees::compute_linear_fee_policy(tx_size, &(fees::PolicyParams::default())) + 1; - println!( - "{} {}", - "\nTx Size Fee:".bright_blue(), - tx_fee.to_string().bright_white() - ); // a max tokens per change output here let change_token_per_utxo: Vec = change_tokens @@ -274,7 +324,7 @@ pub async fn run(args: FundArgs, network_flag: bool, variant: u64) -> Result<()> let number_of_change_utxo: usize = change_token_per_utxo.len(); let mut lovelace_amount: u64 = total_lovelace; for (i, change) in change_token_per_utxo.iter().enumerate() { - let minimum: u64 = wallet_minimum_lovelace_with_assets(change.clone())?; + let minimum: u64 = wallet_minimum_lovelace_with_assets(change.clone()).unwrap_or_default(); let change_lovelace: u64 = if i == number_of_change_utxo - 1 { // this is the last one or the only one lovelace_amount = lovelace_amount - lovelace - tx_fee; @@ -307,12 +357,11 @@ pub async fn run(args: FundArgs, network_flag: bool, variant: u64) -> Result<()> let tx: BuiltTransaction = raw_tx.build_conway_raw().unwrap(); let tx_cbor: String = hex::encode(tx.tx_bytes); - println!("\nTx Cbor: {}", tx_cbor.clone().white()); - - // inject the tx cbor into the local webserver to prompt the wallet - display::webserver_address(); - web_server::run_web_server(tx_cbor, network_flag).await; - text_coloring::display_purple("Server has stopped."); - Ok(()) + // fill this out as we need it + FundSeedelfOutput { + tx_cbor, + tx_fee, + usable_utxos, + } } diff --git a/seedelf-platform/seedelf-cli/src/commands/remove.rs b/seedelf-platform/seedelf-cli/src/commands/remove.rs index 6f36435..aa99afd 100644 --- a/seedelf-platform/seedelf-cli/src/commands/remove.rs +++ b/seedelf-platform/seedelf-cli/src/commands/remove.rs @@ -157,23 +157,13 @@ pub async fn build_remove_seedelf( utxos::get_credential_utxos(config.contract.wallet_contract_hash, network_flag) .await .unwrap_or_default(); - -// for (i, u) in every_utxo.iter().enumerate() { -// println!( -// "[GUI] UTxO {}: tx_hash={} datum={:?}", -// i, -// u.tx_hash, -// u.inline_datum -// ); -// } -// println!("Seedelf: {:}", seedelf); let seedelf_utxo: UtxoResponse = match utxos::find_seedelf_utxo( seedelf.clone(), &config.contract.seedelf_policy_id, every_utxo, ) { Ok(Some(utxo)) => utxo, - _ => UtxoResponse::default() + _ => UtxoResponse::default(), }; let seedelf_datum: Register = extract_bytes_with_logging(&seedelf_utxo.inline_datum) diff --git a/seedelf-platform/seedelf-core/src/transaction.rs b/seedelf-platform/seedelf-core/src/transaction.rs index eb6f854..6782cb9 100644 --- a/seedelf-platform/seedelf-core/src/transaction.rs +++ b/seedelf-platform/seedelf-core/src/transaction.rs @@ -158,13 +158,13 @@ pub fn extract_budgets(value: &Value) -> Vec<(u64, u64)> { // Ensure the value contains the expected "result" array if let Some(result_array) = value.get("result").and_then(|r| r.as_array()) { for item in result_array { - if let Some(budget) = item.get("budget") { - if let (Some(cpu), Some(memory)) = ( + if let Some(budget) = item.get("budget") + && let (Some(cpu), Some(memory)) = ( budget.get("cpu").and_then(|c| c.as_u64()), budget.get("memory").and_then(|m| m.as_u64()), - ) { - budgets.push((cpu, memory)); - } + ) + { + budgets.push((cpu, memory)); } } } diff --git a/seedelf-platform/seedelf-core/src/utxos.rs b/seedelf-platform/seedelf-core/src/utxos.rs index d52ca61..e3e0c84 100644 --- a/seedelf-platform/seedelf-core/src/utxos.rs +++ b/seedelf-platform/seedelf-core/src/utxos.rs @@ -315,24 +315,24 @@ pub fn assets_of(utxos: Vec) -> Result<(u64, Assets)> { let value: u64 = string_to_u64(utxo.value.clone()).context("Invalid UTxO Value")?; current_lovelace_sum += value; - if let Some(assets) = utxo.clone().asset_list { - if !assets.is_empty() { - let mut utxo_assets: Assets = Assets::new(); - - for token in assets.clone() { - let new_asset = Asset::new( - token.policy_id, - token.asset_name, - string_to_u64(token.quantity).context("Invalid Token Quantity")?, - ) - .context("Fail To Construct Asset")?; - utxo_assets = utxo_assets.add(new_asset).context("Can't Add Assets")?; - } - - found_assets = found_assets - .merge(utxo_assets.clone()) - .context("Can't Merge Assets")?; + if let Some(assets) = utxo.clone().asset_list + && !assets.is_empty() + { + let mut utxo_assets: Assets = Assets::new(); + + for token in assets.clone() { + let new_asset = Asset::new( + token.policy_id, + token.asset_name, + string_to_u64(token.quantity).context("Invalid Token Quantity")?, + ) + .context("Fail To Construct Asset")?; + utxo_assets = utxo_assets.add(new_asset).context("Can't Add Assets")?; } + + found_assets = found_assets + .merge(utxo_assets.clone()) + .context("Can't Merge Assets")?; } } Ok((current_lovelace_sum, found_assets)) diff --git a/seedelf-platform/seedelf-gui/src-tauri/src/commands/fund.rs b/seedelf-platform/seedelf-gui/src-tauri/src/commands/fund.rs new file mode 100644 index 0000000..6876f80 --- /dev/null +++ b/seedelf-platform/seedelf-gui/src-tauri/src/commands/fund.rs @@ -0,0 +1,36 @@ +use seedelf_cli::commands::fund::{FundSeedelfOutput, build_fund_seedelf}; +use seedelf_core::assets::Assets; +use seedelf_core::constants::{Config, VARIANT, get_config}; + +#[tauri::command(async)] +pub async fn fund_seedelf( + network_flag: bool, + user_address: String, + seedelf: String, + lovelace: u64, +) -> String { + let config: Config = match get_config(VARIANT, network_flag) { + Some(c) => c, + None => { + return String::new(); + } + }; + let FundSeedelfOutput { + tx_cbor, + usable_utxos, + .. + } = build_fund_seedelf( + config, + network_flag, + user_address, + seedelf, + lovelace, + // todo add in assets + Assets::new(), + ) + .await; + if usable_utxos.is_empty() { + return String::new(); + } + tx_cbor +} diff --git a/seedelf-platform/seedelf-gui/src-tauri/src/commands/mod.rs b/seedelf-platform/seedelf-gui/src-tauri/src/commands/mod.rs index 7199a5c..0008f5e 100644 --- a/seedelf-platform/seedelf-gui/src-tauri/src/commands/mod.rs +++ b/seedelf-platform/seedelf-gui/src-tauri/src/commands/mod.rs @@ -1,2 +1,3 @@ pub mod create; +pub mod fund; pub mod remove; diff --git a/seedelf-platform/seedelf-gui/src-tauri/src/commands/remove.rs b/seedelf-platform/seedelf-gui/src-tauri/src/commands/remove.rs index 5a1f268..ffee93d 100644 --- a/seedelf-platform/seedelf-gui/src-tauri/src/commands/remove.rs +++ b/seedelf-platform/seedelf-gui/src-tauri/src/commands/remove.rs @@ -1,6 +1,6 @@ -use seedelf_core::constants::{Config, VARIANT, get_config}; use crate::session; -use seedelf_cli::commands::remove::{build_remove_seedelf, RemoveSeedelfOutput}; +use seedelf_cli::commands::remove::{RemoveSeedelfOutput, build_remove_seedelf}; +use seedelf_core::constants::{Config, VARIANT, get_config}; #[tauri::command(async)] pub async fn remove_seedelf(network_flag: bool, addr: String, seedelf: String) -> String { @@ -18,7 +18,8 @@ pub async fn remove_seedelf(network_flag: bool, addr: String, seedelf: String) - spend_cpu_units, spend_mem_units, .. - } = match session::with_key(|sk| build_remove_seedelf(config, network_flag, addr, seedelf, *sk)) { + } = match session::with_key(|sk| build_remove_seedelf(config, network_flag, addr, seedelf, *sk)) + { Ok(v) => v.await, _ => return String::new(), }; diff --git a/seedelf-platform/seedelf-gui/src-tauri/src/lib.rs b/seedelf-platform/seedelf-gui/src-tauri/src/lib.rs index 96f5935..09521ee 100644 --- a/seedelf-platform/seedelf-gui/src-tauri/src/lib.rs +++ b/seedelf-platform/seedelf-gui/src-tauri/src/lib.rs @@ -3,6 +3,7 @@ mod commands; pub mod session; pub mod setup; pub mod types; +pub mod utxos; pub mod wallet; pub mod webserver; @@ -25,11 +26,14 @@ pub fn run() { wallet::get_owned_seedelfs, wallet::get_lovelace_balance, wallet::get_wallet_history, + // utxos.rs + utxos::get_every_seedelf, // address.rs address::is_not_a_script, // commands commands::create::create_seedelf, commands::remove::remove_seedelf, + commands::fund::fund_seedelf, // webserver.rs webserver::open_web_server, webserver::close_web_server diff --git a/seedelf-platform/seedelf-gui/src-tauri/src/utxos.rs b/seedelf-platform/seedelf-gui/src-tauri/src/utxos.rs new file mode 100644 index 0000000..f78400d --- /dev/null +++ b/seedelf-platform/seedelf-gui/src-tauri/src/utxos.rs @@ -0,0 +1,16 @@ +use seedelf_core::constants::{Config, VARIANT, get_config}; +use seedelf_core::utxos; +use seedelf_koios::koios::UtxoResponse; + +#[tauri::command(async)] +pub async fn get_every_seedelf(network_flag: bool, all_utxos: Vec) -> Vec { + let config: Config = match get_config(VARIANT, network_flag) { + Some(c) => c, + None => { + return Vec::new(); + } + }; + + utxos::find_all_seedelfs(String::new(), &config.contract.seedelf_policy_id, all_utxos) + .unwrap_or_default() +} diff --git a/seedelf-platform/seedelf-gui/src/components/Checkbox.tsx b/seedelf-platform/seedelf-gui/src/components/Checkbox.tsx new file mode 100644 index 0000000..8cefea5 --- /dev/null +++ b/seedelf-platform/seedelf-gui/src/components/Checkbox.tsx @@ -0,0 +1,58 @@ +import { InputHTMLAttributes } from "react"; + +type CheckboxProps = { + label?: string; + checked: boolean; + onCheckedChange: (checked: boolean) => void; + baseColor: string; +} & Omit< + InputHTMLAttributes, + "type" | "checked" | "onChange" +>; + +export function Checkbox({ + label, + checked, + onCheckedChange, + baseColor, + ...props +}: CheckboxProps) { + return ( + + ); +} diff --git a/seedelf-platform/seedelf-gui/src/components/ExplorerLinkModal.tsx b/seedelf-platform/seedelf-gui/src/components/ExplorerLinkModal.tsx index 7033f34..a5368e0 100644 --- a/seedelf-platform/seedelf-gui/src/components/ExplorerLinkModal.tsx +++ b/seedelf-platform/seedelf-gui/src/components/ExplorerLinkModal.tsx @@ -3,10 +3,7 @@ import { openUrl } from "@tauri-apps/plugin-opener"; import { useNetwork } from "@/types/network"; import { ShowNotification } from "@/components/ShowNotification"; -import { - Link, - Copy, -} from "lucide-react"; +import { Link, Copy } from "lucide-react"; type ExplorerModalProps = { open: boolean; @@ -20,10 +17,13 @@ function txUrl(txHash: string, network: string) { : `https://preprod.cardanoscan.io/transaction/${txHash}`; } -export function ExplorerLinkModal({ open, txHash, onClose }: ExplorerModalProps) { +export function ExplorerLinkModal({ + open, + txHash, + onClose, +}: ExplorerModalProps) { const { network } = useNetwork(); - const [message, setMessage] = useState(null); - + const [message, setMessage] = useState(null); useEffect(() => { if (!open) return; @@ -56,11 +56,17 @@ export function ExplorerLinkModal({ open, txHash, onClose }: ExplorerModalProps) aria-labelledby="modal-title" className="inline-block w-fit max-w-[90vw] rounded-xl bg-gray-800 p-6 shadow-lg" > - - @@ -68,13 +74,13 @@ export function ExplorerLinkModal({ open, txHash, onClose }: ExplorerModalProps) {/* Use Tauri opener so the link opens in the system browser */} {txHash} ); diff --git a/seedelf-platform/seedelf-gui/src/components/TopNavBar.tsx b/seedelf-platform/seedelf-gui/src/components/TopNavBar.tsx index 122ff96..1e88ae4 100644 --- a/seedelf-platform/seedelf-gui/src/components/TopNavBar.tsx +++ b/seedelf-platform/seedelf-gui/src/components/TopNavBar.tsx @@ -17,6 +17,7 @@ function formatAgo(ms: number) { return `${d}d ${hr % 24}h ${min % 60}m ${sec % 60}s ago`; } +// there has to be a better way for this and probably should align with colorClasses const selectStyles = { control: (base: any, state: any) => ({ ...base, diff --git a/seedelf-platform/seedelf-gui/src/components/WebServerModal.tsx b/seedelf-platform/seedelf-gui/src/components/WebServerModal.tsx index 9bc9174..818cbbe 100644 --- a/seedelf-platform/seedelf-gui/src/components/WebServerModal.tsx +++ b/seedelf-platform/seedelf-gui/src/components/WebServerModal.tsx @@ -1,11 +1,7 @@ import { useEffect, useState } from "react"; import { openUrl } from "@tauri-apps/plugin-opener"; import { stopWebServer } from "@pages/Wallet/webServer"; -import { - Link, - CircleQuestionMark, - Copy, -} from "lucide-react"; +import { Link, CircleQuestionMark, Copy } from "lucide-react"; import { ShowNotification } from "@/components/ShowNotification"; type WebServerModalProps = { @@ -16,7 +12,7 @@ type WebServerModalProps = { export function WebServerModal({ open, url, onClose }: WebServerModalProps) { const [message, setMessage] = useState(null); - + useEffect(() => { if (!open) return; const onKey = (e: KeyboardEvent) => e.key === "Escape" && onClose(); @@ -48,8 +44,18 @@ export function WebServerModal({ open, url, onClose }: WebServerModalProps) { aria-labelledby="modal-title" className="inline-block w-fit max-w-[90vw] rounded-xl bg-gray-800 p-6 shadow-lg" > -

-

+ +

+ @@ -57,13 +63,13 @@ export function WebServerModal({ open, url, onClose }: WebServerModalProps) { {/* Use Tauri opener so the link opens in the system browser */} {url} + + + +
+
+ { + const next = e.target.value; + setSeedelf(next); + handleSeedelfExist(next); + }} + disabled={submitting} + maxLength={64} + minLength={64} + /> + + +
+
+ +
+ +
+ +
+ { + if (isSelfSend) { + setSeedelf(""); + setSeedelfExist(false); + setIsSelfSend(false); + } else { + setSeedelf(selfSeedelf); + setSeedelfExist(true); + setIsSelfSend(true); + } + }} + baseColor={colorClasses.green.text} + /> + +
+ + + {(address.length != 0 || seedelf.length != 0 || ada > 0) && ( + + )} +
+
); } diff --git a/seedelf-platform/seedelf-gui/src/pages/Wallet/Manage.tsx b/seedelf-platform/seedelf-gui/src/pages/Wallet/Manage.tsx index 91401ca..f832560 100644 --- a/seedelf-platform/seedelf-gui/src/pages/Wallet/Manage.tsx +++ b/seedelf-platform/seedelf-gui/src/pages/Wallet/Manage.tsx @@ -21,18 +21,19 @@ export function Manage() { const [seedelf, setSeedelf] = useState(""); const [txHash, setTxHash] = useState(""); - + const [message, setMessage] = useState(null); const [variant, setVariant] = useState("error"); - + const [submitting, setSubmitting] = useState(false); - + const [showWebServerModal, setShowWebServerModal] = useState(false); - const [showExplorerLinkModal, setShowExplorerLinkModal] = useState(false); + const [showExplorerLinkModal, setShowExplorerLinkModal] = + useState(false); const [mode, setMode] = useState("Create"); - + const { network } = useNetwork(); - const { seedelfs } = useOutletContext(); + const { ownedSeedelfs } = useOutletContext(); const selectSeedelf = async (text: string) => { setVariant("info"); @@ -111,20 +112,20 @@ export function Manage() { /> { setVariant("info"); setMessage("Stopping Web Server.."); - setShowWebServerModal(false) + setShowWebServerModal(false); }} /> { - setShowExplorerLinkModal(false) + setShowExplorerLinkModal(false); }} /> @@ -187,13 +188,13 @@ export function Manage() {
- {seedelfs.length === 0 || mode == "Create" ? ( + {ownedSeedelfs.length === 0 || mode == "Create" ? ( <> ) : (
    - {seedelfs.map((h) => ( + {ownedSeedelfs.map((h) => (
  • {h} diff --git a/seedelf-platform/seedelf-gui/src/pages/Wallet/WalletLayout.tsx b/seedelf-platform/seedelf-gui/src/pages/Wallet/WalletLayout.tsx index 90d7083..357029b 100644 --- a/seedelf-platform/seedelf-gui/src/pages/Wallet/WalletLayout.tsx +++ b/seedelf-platform/seedelf-gui/src/pages/Wallet/WalletLayout.tsx @@ -16,6 +16,7 @@ import { getEveryUtxo, getOwnedUtxo, getOwnedSeedelfs, + getEverySeedelf, } from "./api"; export function WalletPage() { @@ -26,9 +27,15 @@ export function WalletPage() { // wallet states const [lovelace, setLovelace] = useState(0); - const [seedelfs, setSeedelfs] = useState([]); + const [allSeedelfs, setAllSeedelfs] = useState([]); + const [ownedSeedelfs, setOwnedSeedelfs] = useState([]); const [history, setHistory] = useState([]); + // toast + const [toastMsg, setToastMsg] = useState(null); + const [toastDur, setToastDur] = useState(2718); + const [toastVariant, setToastVariant] = useState("info"); + // network selector const [network, setNetwork] = useState( () => (localStorage.getItem("network") as Network) || "mainnet", @@ -37,21 +44,32 @@ export function WalletPage() { const gatherWalletInfo = async () => { // initalize stuff setLovelace(0); - setSeedelfs([]); + setOwnedSeedelfs([]); setHistory([]); // query stuff + setToastVariant("info"); + setToastDur(10000); + setToastMsg("Getting Wallet History"); const _history = await getWalletHistory(network); + setToastMsg("Querying Wallet UTxOs"); const _every_utxo = await getEveryUtxo(network); - + setToastMsg("Sorting Owned UTxOs"); const _owned_utxo = await getOwnedUtxo(network, _every_utxo); - const _seedelfs = await getOwnedSeedelfs(network, _every_utxo); - + setToastMsg("Sorting All Seedelfs"); + const _allSeedelfs = await getEverySeedelf(network, _every_utxo); + setToastMsg("Sorting Owned Seedelfs"); + const _ownedSeedelfs = await getOwnedSeedelfs(network, _every_utxo); + setToastMsg("Calculating Balance"); const _lovelace = await getLovelaceBalance(_owned_utxo); + setToastVariant("success"); + setToastDur(2718); + setToastMsg("Wallet Loaded"); // set stuff setLovelace(_lovelace); - setSeedelfs(_seedelfs); + setOwnedSeedelfs(_ownedSeedelfs); + setAllSeedelfs(_allSeedelfs); setHistory(_history); // set last sync time @@ -63,17 +81,12 @@ export function WalletPage() { useEffect(() => { localStorage.setItem("network", network); if (unlocked) { - setToastMsg(`Loading Network: ${network}`); setToastVariant("info"); + setToastMsg(`Loading Network: ${network}`); + gatherWalletInfo(); } - - gatherWalletInfo(); }, [network, unlocked]); - // toast - const [toastMsg, setToastMsg] = useState(null); - const [toastVariant, setToastVariant] = useState("info"); - const tryUnlock = async () => { setUnlocking(true); try { @@ -97,6 +110,7 @@ export function WalletPage() { message={toastMsg} setMessage={setToastMsg} variant={toastVariant} + duration={toastDur} /> {!unlocked && ( @@ -138,8 +152,8 @@ export function WalletPage() { lastSync={lastSync} lovelace={lovelace} onRefresh={async () => { - setToastMsg("Refreshing State"); setToastVariant("info"); + setToastMsg("Refreshing State"); gatherWalletInfo(); }} onLock={async () => { @@ -153,7 +167,9 @@ export function WalletPage() {
    - +
diff --git a/seedelf-platform/seedelf-gui/src/pages/Wallet/api.ts b/seedelf-platform/seedelf-gui/src/pages/Wallet/api.ts index bc5d7fa..f557c77 100644 --- a/seedelf-platform/seedelf-gui/src/pages/Wallet/api.ts +++ b/seedelf-platform/seedelf-gui/src/pages/Wallet/api.ts @@ -15,6 +15,17 @@ export async function getEveryUtxo(network: Network): Promise { return await invoke("get_every_utxo", { networkFlag: flag }); } +export async function getEverySeedelf( + network: Network, + allUtxos: UtxoResponse[], +): Promise { + const flag = castNetwork(network); + return invoke("get_every_seedelf", { + networkFlag: flag, + allUtxos: allUtxos, + }); +} + export function getOwnedUtxo( network: Network, everyUtxo: UtxoResponse[], @@ -55,7 +66,9 @@ export async function getWalletHistory( networkFlag: flag, }); // force newest first - const sortedHistory = history.slice().sort((a, b) => b.tx.block_height - a.tx.block_height); + const sortedHistory = history + .slice() + .sort((a, b) => b.tx.block_height - a.tx.block_height); return sortedHistory; } diff --git a/seedelf-platform/seedelf-gui/src/pages/Wallet/colors.ts b/seedelf-platform/seedelf-gui/src/pages/Wallet/colors.ts index bfe75da..2caf694 100644 --- a/seedelf-platform/seedelf-gui/src/pages/Wallet/colors.ts +++ b/seedelf-platform/seedelf-gui/src/pages/Wallet/colors.ts @@ -19,4 +19,12 @@ export const colorClasses: any = { text: "text-slate-600 hover:text-slate-700", bg: "bg-slate-600 hover:bg-slate-700", }, + green: { + text: "text-green-600 hover:text-green-700", + bg: "bg-green-600 hover:bg-green-700", + }, + red: { + text: "text-red-600 hover:text-red-700", + bg: "bg-red-600 hover:bg-red-700", + }, }; diff --git a/seedelf-platform/seedelf-gui/src/pages/Wallet/transactions.ts b/seedelf-platform/seedelf-gui/src/pages/Wallet/transactions.ts index 79becd0..ff93000 100644 --- a/seedelf-platform/seedelf-gui/src/pages/Wallet/transactions.ts +++ b/seedelf-platform/seedelf-gui/src/pages/Wallet/transactions.ts @@ -2,7 +2,6 @@ import { invoke } from "@tauri-apps/api/core"; import { Network } from "@/types/network"; import { castNetwork } from "./api"; - export async function createSeedelf( network: Network, addr: string, @@ -12,7 +11,7 @@ export async function createSeedelf( return await invoke("create_seedelf", { networkFlag: flag, addr: addr, - label: label + label: label, }); } @@ -25,6 +24,21 @@ export async function removeSeedelf( return await invoke("remove_seedelf", { networkFlag: flag, addr: addr, - seedelf: seedelf + seedelf: seedelf, }); -} \ No newline at end of file +} + +export async function fundSeedelf( + network: Network, + addr: string, + seedelf: string, + lovelace: number, +): Promise { + const flag = castNetwork(network); + return await invoke("fund_seedelf", { + networkFlag: flag, + userAddress: addr, + seedelf: seedelf, + lovelace: lovelace, + }); +} diff --git a/seedelf-platform/seedelf-gui/src/pages/Wallet/webServer.ts b/seedelf-platform/seedelf-gui/src/pages/Wallet/webServer.ts index 0e399c1..6d030a8 100644 --- a/seedelf-platform/seedelf-gui/src/pages/Wallet/webServer.ts +++ b/seedelf-platform/seedelf-gui/src/pages/Wallet/webServer.ts @@ -2,11 +2,17 @@ import { invoke } from "@tauri-apps/api/core"; import { Network } from "@/types/network"; import { castNetwork } from "./api"; -export async function runWebServer(tx_cbor: string, network: Network): Promise { +export async function runWebServer( + tx_cbor: string, + network: Network, +): Promise { const flag = castNetwork(network); - return await invoke("open_web_server", {txCbor: tx_cbor, networkFlag: flag}) + return await invoke("open_web_server", { + txCbor: tx_cbor, + networkFlag: flag, + }); } export async function stopWebServer() { await invoke("close_web_server"); -} \ No newline at end of file +} diff --git a/seedelf-platform/seedelf-gui/src/types/layout.ts b/seedelf-platform/seedelf-gui/src/types/layout.ts index 391be7c..b4f3f85 100644 --- a/seedelf-platform/seedelf-gui/src/types/layout.ts +++ b/seedelf-platform/seedelf-gui/src/types/layout.ts @@ -3,6 +3,7 @@ import { TxResponseWithSide } from "./wallet"; // to be passed into the outlets export type OutletContextType = { lovelace: number; - seedelfs: string[]; + allSeedelfs: string[]; + ownedSeedelfs: string[]; history: TxResponseWithSide[]; };