From c22d6c953e19335165b22d84b9fef39763fa9195 Mon Sep 17 00:00:00 2001 From: Timur Gordon <31495328+timurgordon@users.noreply.github.com> Date: Sat, 26 Apr 2025 03:44:36 +0200 Subject: [PATCH 01/12] implement marketplace feature wip --- actix_mvc_app/src/controllers/asset.rs | 2 +- actix_mvc_app/src/controllers/marketplace.rs | 545 ++++++++++++++++++ actix_mvc_app/src/controllers/mod.rs | 1 + actix_mvc_app/src/models/marketplace.rs | 295 ++++++++++ actix_mvc_app/src/models/mod.rs | 2 + actix_mvc_app/src/routes/mod.rs | 15 + actix_mvc_app/src/views/base.html | 6 + .../src/views/marketplace/create_listing.html | 236 ++++++++ .../src/views/marketplace/index.html | 293 ++++++++++ .../src/views/marketplace/listing_detail.html | 350 +++++++++++ .../src/views/marketplace/listings.html | 294 ++++++++++ .../src/views/marketplace/my_listings.html | 238 ++++++++ 12 files changed, 2276 insertions(+), 1 deletion(-) create mode 100644 actix_mvc_app/src/controllers/marketplace.rs create mode 100644 actix_mvc_app/src/models/marketplace.rs create mode 100644 actix_mvc_app/src/views/marketplace/create_listing.html create mode 100644 actix_mvc_app/src/views/marketplace/index.html create mode 100644 actix_mvc_app/src/views/marketplace/listing_detail.html create mode 100644 actix_mvc_app/src/views/marketplace/listings.html create mode 100644 actix_mvc_app/src/views/marketplace/my_listings.html diff --git a/actix_mvc_app/src/controllers/asset.rs b/actix_mvc_app/src/controllers/asset.rs index 7faed45..aae8100 100644 --- a/actix_mvc_app/src/controllers/asset.rs +++ b/actix_mvc_app/src/controllers/asset.rs @@ -443,7 +443,7 @@ impl AssetController { } // Generate mock assets for testing - fn get_mock_assets() -> Vec { + pub fn get_mock_assets() -> Vec { let now = Utc::now(); let mut assets = Vec::new(); diff --git a/actix_mvc_app/src/controllers/marketplace.rs b/actix_mvc_app/src/controllers/marketplace.rs new file mode 100644 index 0000000..ebe48d5 --- /dev/null +++ b/actix_mvc_app/src/controllers/marketplace.rs @@ -0,0 +1,545 @@ +use actix_web::{web, HttpResponse, Result, http}; +use tera::{Context, Tera}; +use chrono::{Utc, Duration}; +use serde::Deserialize; +use uuid::Uuid; + +use crate::models::asset::{Asset, AssetType, AssetStatus}; +use crate::models::marketplace::{Listing, ListingStatus, ListingType, Bid, BidStatus, MarketplaceStatistics}; +use crate::controllers::asset::AssetController; +use crate::utils::render_template; + +#[derive(Debug, Deserialize)] +pub struct ListingForm { + pub title: String, + pub description: String, + pub asset_id: String, + pub price: f64, + pub currency: String, + pub listing_type: String, + pub duration_days: Option, + pub tags: Option, +} + +#[derive(Debug, Deserialize)] +pub struct BidForm { + pub amount: f64, + pub currency: String, +} + +#[derive(Debug, Deserialize)] +pub struct PurchaseForm { + pub agree_to_terms: bool, +} + +pub struct MarketplaceController; + +impl MarketplaceController { + // Display the marketplace dashboard + pub async fn index(tmpl: web::Data) -> Result { + let mut context = Context::new(); + + let listings = Self::get_mock_listings(); + let stats = MarketplaceStatistics::new(&listings); + + // Get featured listings (up to 4) + let featured_listings: Vec<&Listing> = listings.iter() + .filter(|l| l.featured && l.status == ListingStatus::Active) + .take(4) + .collect(); + + // Get recent listings (up to 8) + let mut recent_listings: Vec<&Listing> = listings.iter() + .filter(|l| l.status == ListingStatus::Active) + .collect(); + + // Sort by created_at (newest first) + recent_listings.sort_by(|a, b| b.created_at.cmp(&a.created_at)); + let recent_listings = recent_listings.into_iter().take(8).collect::>(); + + // Get recent sales (up to 5) + let mut recent_sales: Vec<&Listing> = listings.iter() + .filter(|l| l.status == ListingStatus::Sold) + .collect(); + + // Sort by sold_at (newest first) + recent_sales.sort_by(|a, b| { + let a_sold = a.sold_at.unwrap_or(a.created_at); + let b_sold = b.sold_at.unwrap_or(b.created_at); + b_sold.cmp(&a_sold) + }); + let recent_sales = recent_sales.into_iter().take(5).collect::>(); + + // Add data to context + context.insert("active_page", &"marketplace"); + context.insert("stats", &stats); + context.insert("featured_listings", &featured_listings); + context.insert("recent_listings", &recent_listings); + context.insert("recent_sales", &recent_sales); + + render_template(&tmpl, "marketplace/index.html", &context) + } + + // Display all marketplace listings + pub async fn list_listings(tmpl: web::Data) -> Result { + let mut context = Context::new(); + + let listings = Self::get_mock_listings(); + + // Filter active listings + let active_listings: Vec<&Listing> = listings.iter() + .filter(|l| l.status == ListingStatus::Active) + .collect(); + + context.insert("active_page", &"marketplace"); + context.insert("listings", &active_listings); + context.insert("listing_types", &[ + ListingType::FixedPrice.as_str(), + ListingType::Auction.as_str(), + ListingType::Exchange.as_str(), + ]); + context.insert("asset_types", &[ + AssetType::Token.as_str(), + AssetType::NFT.as_str(), + AssetType::RealEstate.as_str(), + AssetType::IntellectualProperty.as_str(), + AssetType::PhysicalAsset.as_str(), + ]); + + render_template(&tmpl, "marketplace/listings.html", &context) + } + + // Display my listings + pub async fn my_listings(tmpl: web::Data) -> Result { + let mut context = Context::new(); + + let listings = Self::get_mock_listings(); + + // Filter by current user (mock user ID) + let user_id = "user-123"; + let my_listings: Vec<&Listing> = listings.iter() + .filter(|l| l.seller_id == user_id) + .collect(); + + context.insert("active_page", &"marketplace"); + context.insert("listings", &my_listings); + + render_template(&tmpl, "marketplace/my_listings.html", &context) + } + + // Display listing details + pub async fn listing_detail(tmpl: web::Data, path: web::Path) -> Result { + let listing_id = path.into_inner(); + let mut context = Context::new(); + + let listings = Self::get_mock_listings(); + + // Find the listing + let listing = listings.iter().find(|l| l.id == listing_id); + + if let Some(listing) = listing { + // Get similar listings (same asset type, active) + let similar_listings: Vec<&Listing> = listings.iter() + .filter(|l| l.asset_type == listing.asset_type && + l.status == ListingStatus::Active && + l.id != listing.id) + .take(4) + .collect(); + + context.insert("active_page", &"marketplace"); + context.insert("listing", listing); + context.insert("similar_listings", &similar_listings); + + // Add current user info for bid/purchase forms + let user_id = "user-123"; + let user_name = "John Doe"; + context.insert("user_id", &user_id); + context.insert("user_name", &user_name); + + render_template(&tmpl, "marketplace/listing_detail.html", &context) + } else { + Ok(HttpResponse::NotFound().finish()) + } + } + + // Display create listing form + pub async fn create_listing_form(tmpl: web::Data) -> Result { + let mut context = Context::new(); + + // Get user's assets for selection + let assets = AssetController::get_mock_assets(); + let user_id = "user-123"; // Mock user ID + + let user_assets: Vec<&Asset> = assets.iter() + .filter(|a| a.owner_id == user_id && a.status == AssetStatus::Active) + .collect(); + + context.insert("active_page", &"marketplace"); + context.insert("assets", &user_assets); + context.insert("listing_types", &[ + ListingType::FixedPrice.as_str(), + ListingType::Auction.as_str(), + ListingType::Exchange.as_str(), + ]); + + render_template(&tmpl, "marketplace/create_listing.html", &context) + } + + // Create a new listing + pub async fn create_listing( + tmpl: web::Data, + form: web::Form, + ) -> Result { + let form = form.into_inner(); + + // Get the asset details + let assets = AssetController::get_mock_assets(); + let asset = assets.iter().find(|a| a.id == form.asset_id); + + if let Some(asset) = asset { + // Process tags + let tags = match form.tags { + Some(tags_str) => tags_str.split(',') + .map(|s| s.trim().to_string()) + .filter(|s| !s.is_empty()) + .collect(), + None => Vec::new(), + }; + + // Calculate expiration date if provided + let expires_at = form.duration_days.map(|days| { + Utc::now() + Duration::days(days as i64) + }); + + // Parse listing type + let listing_type = match form.listing_type.as_str() { + "Fixed Price" => ListingType::FixedPrice, + "Auction" => ListingType::Auction, + "Exchange" => ListingType::Exchange, + _ => ListingType::FixedPrice, + }; + + // Mock user data + let user_id = "user-123"; + let user_name = "John Doe"; + + // Create the listing + let _listing = Listing::new( + form.title, + form.description, + asset.id.clone(), + asset.name.clone(), + asset.asset_type.clone(), + user_id.to_string(), + user_name.to_string(), + form.price, + form.currency, + listing_type, + expires_at, + tags, + asset.image_url.clone(), + ); + + // In a real application, we would save the listing to a database here + + // Redirect to the marketplace + Ok(HttpResponse::SeeOther() + .insert_header((http::header::LOCATION, "/marketplace")) + .finish()) + } else { + // Asset not found + let mut context = Context::new(); + context.insert("active_page", &"marketplace"); + context.insert("error", &"Asset not found"); + + render_template(&tmpl, "marketplace/create_listing.html", &context) + } + } + + // Submit a bid on an auction listing + pub async fn submit_bid( + tmpl: web::Data, + path: web::Path, + form: web::Form, + ) -> Result { + let listing_id = path.into_inner(); + let form = form.into_inner(); + + // In a real application, we would: + // 1. Find the listing in the database + // 2. Validate the bid + // 3. Create the bid + // 4. Save it to the database + + // For now, we'll just redirect back to the listing + Ok(HttpResponse::SeeOther() + .insert_header((http::header::LOCATION, format!("/marketplace/{}", listing_id))) + .finish()) + } + + // Purchase a fixed-price listing + pub async fn purchase_listing( + tmpl: web::Data, + path: web::Path, + form: web::Form, + ) -> Result { + let listing_id = path.into_inner(); + let form = form.into_inner(); + + if !form.agree_to_terms { + // User must agree to terms + return Ok(HttpResponse::SeeOther() + .insert_header((http::header::LOCATION, format!("/marketplace/{}", listing_id))) + .finish()); + } + + // In a real application, we would: + // 1. Find the listing in the database + // 2. Validate the purchase + // 3. Process the transaction + // 4. Update the listing status + // 5. Transfer the asset + + // For now, we'll just redirect to the marketplace + Ok(HttpResponse::SeeOther() + .insert_header((http::header::LOCATION, "/marketplace")) + .finish()) + } + + // Cancel a listing + pub async fn cancel_listing( + tmpl: web::Data, + path: web::Path, + ) -> Result { + let listing_id = path.into_inner(); + + // In a real application, we would: + // 1. Find the listing in the database + // 2. Validate that the current user is the seller + // 3. Update the listing status + + // For now, we'll just redirect to my listings + Ok(HttpResponse::SeeOther() + .insert_header((http::header::LOCATION, "/marketplace/my")) + .finish()) + } + + // Generate mock listings for development + pub fn get_mock_listings() -> Vec { + let assets = AssetController::get_mock_assets(); + let mut listings = Vec::new(); + + // Mock user data + let user_ids = vec!["user-123", "user-456", "user-789"]; + let user_names = vec!["John Doe", "Jane Smith", "Bob Johnson"]; + + // Create some fixed price listings + for i in 0..6 { + let asset_index = i % assets.len(); + let asset = &assets[asset_index]; + let user_index = i % user_ids.len(); + + let price = match asset.asset_type { + AssetType::Token => 50.0 + (i as f64 * 10.0), + AssetType::NFT => 500.0 + (i as f64 * 100.0), + AssetType::RealEstate => 50000.0 + (i as f64 * 10000.0), + AssetType::IntellectualProperty => 2000.0 + (i as f64 * 500.0), + AssetType::PhysicalAsset => 1000.0 + (i as f64 * 200.0), + }; + + let mut listing = Listing::new( + format!("{} for Sale", asset.name), + format!("This is a great opportunity to own {}. {}", asset.name, asset.description), + asset.id.clone(), + asset.name.clone(), + asset.asset_type.clone(), + user_ids[user_index].to_string(), + user_names[user_index].to_string(), + price, + "USD".to_string(), + ListingType::FixedPrice, + Some(Utc::now() + Duration::days(30)), + vec!["digital".to_string(), "asset".to_string()], + asset.image_url.clone(), + ); + + // Make some listings featured + if i % 5 == 0 { + listing.set_featured(true); + } + + listings.push(listing); + } + + // Create some auction listings + for i in 0..4 { + let asset_index = (i + 6) % assets.len(); + let asset = &assets[asset_index]; + let user_index = i % user_ids.len(); + + let starting_price = match asset.asset_type { + AssetType::Token => 40.0 + (i as f64 * 5.0), + AssetType::NFT => 400.0 + (i as f64 * 50.0), + AssetType::RealEstate => 40000.0 + (i as f64 * 5000.0), + AssetType::IntellectualProperty => 1500.0 + (i as f64 * 300.0), + AssetType::PhysicalAsset => 800.0 + (i as f64 * 100.0), + }; + + let mut listing = Listing::new( + format!("Auction: {}", asset.name), + format!("Bid on this amazing {}. {}", asset.name, asset.description), + asset.id.clone(), + asset.name.clone(), + asset.asset_type.clone(), + user_ids[user_index].to_string(), + user_names[user_index].to_string(), + starting_price, + "USD".to_string(), + ListingType::Auction, + Some(Utc::now() + Duration::days(7)), + vec!["auction".to_string(), "bidding".to_string()], + asset.image_url.clone(), + ); + + // Add some bids to the auctions + let num_bids = 2 + (i % 3); + for j in 0..num_bids { + let bidder_index = (j + 1) % user_ids.len(); + if bidder_index != user_index { // Ensure seller isn't bidding + let bid_amount = starting_price * (1.0 + (0.1 * (j + 1) as f64)); + let _ = listing.add_bid( + user_ids[bidder_index].to_string(), + user_names[bidder_index].to_string(), + bid_amount, + "USD".to_string(), + ); + } + } + + // Make some listings featured + if i % 3 == 0 { + listing.set_featured(true); + } + + listings.push(listing); + } + + // Create some exchange listings + for i in 0..3 { + let asset_index = (i + 10) % assets.len(); + let asset = &assets[asset_index]; + let user_index = i % user_ids.len(); + + let value = match asset.asset_type { + AssetType::Token => 60.0 + (i as f64 * 15.0), + AssetType::NFT => 600.0 + (i as f64 * 150.0), + AssetType::RealEstate => 60000.0 + (i as f64 * 15000.0), + AssetType::IntellectualProperty => 2500.0 + (i as f64 * 600.0), + AssetType::PhysicalAsset => 1200.0 + (i as f64 * 300.0), + }; + + let listing = Listing::new( + format!("Trade: {}", asset.name), + format!("Looking to exchange {} for another asset of similar value. Interested in NFTs and tokens.", asset.name), + asset.id.clone(), + asset.name.clone(), + asset.asset_type.clone(), + user_ids[user_index].to_string(), + user_names[user_index].to_string(), + value, // Estimated value for exchange + "USD".to_string(), + ListingType::Exchange, + Some(Utc::now() + Duration::days(60)), + vec!["exchange".to_string(), "trade".to_string()], + asset.image_url.clone(), + ); + + listings.push(listing); + } + + // Create some sold listings + for i in 0..5 { + let asset_index = (i + 13) % assets.len(); + let asset = &assets[asset_index]; + let seller_index = i % user_ids.len(); + let buyer_index = (i + 1) % user_ids.len(); + + let price = match asset.asset_type { + AssetType::Token => 55.0 + (i as f64 * 12.0), + AssetType::NFT => 550.0 + (i as f64 * 120.0), + AssetType::RealEstate => 55000.0 + (i as f64 * 12000.0), + AssetType::IntellectualProperty => 2200.0 + (i as f64 * 550.0), + AssetType::PhysicalAsset => 1100.0 + (i as f64 * 220.0), + }; + + let sale_price = price * 0.95; // Slight discount on sale + + let mut listing = Listing::new( + format!("{} - SOLD", asset.name), + format!("This {} was sold recently.", asset.name), + asset.id.clone(), + asset.name.clone(), + asset.asset_type.clone(), + user_ids[seller_index].to_string(), + user_names[seller_index].to_string(), + price, + "USD".to_string(), + ListingType::FixedPrice, + None, + vec!["sold".to_string()], + asset.image_url.clone(), + ); + + // Mark as sold + let _ = listing.mark_as_sold( + user_ids[buyer_index].to_string(), + user_names[buyer_index].to_string(), + sale_price, + ); + + // Set sold date to be sometime in the past + let days_ago = i as i64 + 1; + listing.sold_at = Some(Utc::now() - Duration::days(days_ago)); + + listings.push(listing); + } + + // Create a few cancelled listings + for i in 0..2 { + let asset_index = (i + 18) % assets.len(); + let asset = &assets[asset_index]; + let user_index = i % user_ids.len(); + + let price = match asset.asset_type { + AssetType::Token => 45.0 + (i as f64 * 8.0), + AssetType::NFT => 450.0 + (i as f64 * 80.0), + AssetType::RealEstate => 45000.0 + (i as f64 * 8000.0), + AssetType::IntellectualProperty => 1800.0 + (i as f64 * 400.0), + AssetType::PhysicalAsset => 900.0 + (i as f64 * 180.0), + }; + + let mut listing = Listing::new( + format!("{} - Cancelled", asset.name), + format!("This listing for {} was cancelled.", asset.name), + asset.id.clone(), + asset.name.clone(), + asset.asset_type.clone(), + user_ids[user_index].to_string(), + user_names[user_index].to_string(), + price, + "USD".to_string(), + ListingType::FixedPrice, + None, + vec!["cancelled".to_string()], + asset.image_url.clone(), + ); + + // Cancel the listing + let _ = listing.cancel(); + + listings.push(listing); + } + + listings + } +} diff --git a/actix_mvc_app/src/controllers/mod.rs b/actix_mvc_app/src/controllers/mod.rs index 22cf289..18054d0 100644 --- a/actix_mvc_app/src/controllers/mod.rs +++ b/actix_mvc_app/src/controllers/mod.rs @@ -7,5 +7,6 @@ pub mod governance; pub mod flow; pub mod contract; pub mod asset; +pub mod marketplace; // Re-export controllers for easier imports diff --git a/actix_mvc_app/src/models/marketplace.rs b/actix_mvc_app/src/models/marketplace.rs new file mode 100644 index 0000000..784a53b --- /dev/null +++ b/actix_mvc_app/src/models/marketplace.rs @@ -0,0 +1,295 @@ +use chrono::{DateTime, Utc}; +use serde::{Deserialize, Serialize}; +use uuid::Uuid; +use crate::models::asset::{Asset, AssetType}; + +/// Status of a marketplace listing +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub enum ListingStatus { + Active, + Sold, + Cancelled, + Expired, +} + +impl ListingStatus { + pub fn as_str(&self) -> &str { + match self { + ListingStatus::Active => "Active", + ListingStatus::Sold => "Sold", + ListingStatus::Cancelled => "Cancelled", + ListingStatus::Expired => "Expired", + } + } +} + +/// Type of marketplace listing +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub enum ListingType { + FixedPrice, + Auction, + Exchange, +} + +impl ListingType { + pub fn as_str(&self) -> &str { + match self { + ListingType::FixedPrice => "Fixed Price", + ListingType::Auction => "Auction", + ListingType::Exchange => "Exchange", + } + } +} + +/// Represents a bid on an auction listing +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Bid { + pub id: String, + pub listing_id: String, + pub bidder_id: String, + pub bidder_name: String, + pub amount: f64, + pub currency: String, + pub created_at: DateTime, + pub status: BidStatus, +} + +/// Status of a bid +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub enum BidStatus { + Active, + Accepted, + Rejected, + Cancelled, +} + +impl BidStatus { + pub fn as_str(&self) -> &str { + match self { + BidStatus::Active => "Active", + BidStatus::Accepted => "Accepted", + BidStatus::Rejected => "Rejected", + BidStatus::Cancelled => "Cancelled", + } + } +} + +/// Represents a marketplace listing +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Listing { + pub id: String, + pub title: String, + pub description: String, + pub asset_id: String, + pub asset_name: String, + pub asset_type: AssetType, + pub seller_id: String, + pub seller_name: String, + pub price: f64, + pub currency: String, + pub listing_type: ListingType, + pub status: ListingStatus, + pub created_at: DateTime, + pub updated_at: DateTime, + pub expires_at: Option>, + pub sold_at: Option>, + pub buyer_id: Option, + pub buyer_name: Option, + pub sale_price: Option, + pub bids: Vec, + pub views: u32, + pub featured: bool, + pub tags: Vec, + pub image_url: Option, +} + +impl Listing { + /// Creates a new listing + pub fn new( + title: String, + description: String, + asset_id: String, + asset_name: String, + asset_type: AssetType, + seller_id: String, + seller_name: String, + price: f64, + currency: String, + listing_type: ListingType, + expires_at: Option>, + tags: Vec, + image_url: Option, + ) -> Self { + let now = Utc::now(); + Self { + id: format!("listing-{}", Uuid::new_v4().to_string()), + title, + description, + asset_id, + asset_name, + asset_type, + seller_id, + seller_name, + price, + currency, + listing_type, + status: ListingStatus::Active, + created_at: now, + updated_at: now, + expires_at, + sold_at: None, + buyer_id: None, + buyer_name: None, + sale_price: None, + bids: Vec::new(), + views: 0, + featured: false, + tags, + image_url, + } + } + + /// Adds a bid to the listing + pub fn add_bid(&mut self, bidder_id: String, bidder_name: String, amount: f64, currency: String) -> Result<(), String> { + if self.status != ListingStatus::Active { + return Err("Listing is not active".to_string()); + } + + if self.listing_type != ListingType::Auction { + return Err("Listing is not an auction".to_string()); + } + + if currency != self.currency { + return Err(format!("Currency mismatch: expected {}, got {}", self.currency, currency)); + } + + // Check if bid amount is higher than current highest bid or starting price + let highest_bid = self.highest_bid(); + let min_bid = match highest_bid { + Some(bid) => bid.amount, + None => self.price, + }; + + if amount <= min_bid { + return Err(format!("Bid amount must be higher than {}", min_bid)); + } + + let bid = Bid { + id: format!("bid-{}", Uuid::new_v4().to_string()), + listing_id: self.id.clone(), + bidder_id, + bidder_name, + amount, + currency, + created_at: Utc::now(), + status: BidStatus::Active, + }; + + self.bids.push(bid); + self.updated_at = Utc::now(); + + Ok(()) + } + + /// Gets the highest bid on the listing + pub fn highest_bid(&self) -> Option<&Bid> { + self.bids.iter() + .filter(|b| b.status == BidStatus::Active) + .max_by(|a, b| a.amount.partial_cmp(&b.amount).unwrap()) + } + + /// Marks the listing as sold + pub fn mark_as_sold(&mut self, buyer_id: String, buyer_name: String, sale_price: f64) -> Result<(), String> { + if self.status != ListingStatus::Active { + return Err("Listing is not active".to_string()); + } + + self.status = ListingStatus::Sold; + self.sold_at = Some(Utc::now()); + self.buyer_id = Some(buyer_id); + self.buyer_name = Some(buyer_name); + self.sale_price = Some(sale_price); + self.updated_at = Utc::now(); + + Ok(()) + } + + /// Cancels the listing + pub fn cancel(&mut self) -> Result<(), String> { + if self.status != ListingStatus::Active { + return Err("Listing is not active".to_string()); + } + + self.status = ListingStatus::Cancelled; + self.updated_at = Utc::now(); + + Ok(()) + } + + /// Increments the view count + pub fn increment_views(&mut self) { + self.views += 1; + } + + /// Sets the listing as featured + pub fn set_featured(&mut self, featured: bool) { + self.featured = featured; + self.updated_at = Utc::now(); + } +} + +/// Statistics for marketplace +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct MarketplaceStatistics { + pub total_listings: usize, + pub active_listings: usize, + pub sold_listings: usize, + pub total_value: f64, + pub total_sales: f64, + pub listings_by_type: std::collections::HashMap, + pub sales_by_asset_type: std::collections::HashMap, +} + +impl MarketplaceStatistics { + pub fn new(listings: &[Listing]) -> Self { + let mut total_value = 0.0; + let mut total_sales = 0.0; + let mut listings_by_type = std::collections::HashMap::new(); + let mut sales_by_asset_type = std::collections::HashMap::new(); + + let active_listings = listings.iter() + .filter(|l| l.status == ListingStatus::Active) + .count(); + + let sold_listings = listings.iter() + .filter(|l| l.status == ListingStatus::Sold) + .count(); + + for listing in listings { + if listing.status == ListingStatus::Active { + total_value += listing.price; + } + + if listing.status == ListingStatus::Sold { + if let Some(sale_price) = listing.sale_price { + total_sales += sale_price; + let asset_type = listing.asset_type.as_str().to_string(); + *sales_by_asset_type.entry(asset_type).or_insert(0.0) += sale_price; + } + } + + let listing_type = listing.listing_type.as_str().to_string(); + *listings_by_type.entry(listing_type).or_insert(0) += 1; + } + + Self { + total_listings: listings.len(), + active_listings, + sold_listings, + total_value, + total_sales, + listings_by_type, + sales_by_asset_type, + } + } +} diff --git a/actix_mvc_app/src/models/mod.rs b/actix_mvc_app/src/models/mod.rs index b2cb5d7..8d7fff0 100644 --- a/actix_mvc_app/src/models/mod.rs +++ b/actix_mvc_app/src/models/mod.rs @@ -6,8 +6,10 @@ pub mod governance; pub mod flow; pub mod contract; pub mod asset; +pub mod marketplace; // Re-export models for easier imports pub use user::User; pub use ticket::{Ticket, TicketComment, TicketStatus, TicketPriority}; pub use calendar::{CalendarEvent, CalendarViewMode}; +pub use marketplace::{Listing, ListingStatus, ListingType, Bid, BidStatus, MarketplaceStatistics}; diff --git a/actix_mvc_app/src/routes/mod.rs b/actix_mvc_app/src/routes/mod.rs index 42595b9..10981da 100644 --- a/actix_mvc_app/src/routes/mod.rs +++ b/actix_mvc_app/src/routes/mod.rs @@ -8,6 +8,7 @@ use crate::controllers::governance::GovernanceController; use crate::controllers::flow::FlowController; use crate::controllers::contract::ContractController; use crate::controllers::asset::AssetController; +use crate::controllers::marketplace::MarketplaceController; use crate::middleware::JwtAuth; use crate::SESSION_KEY; @@ -105,6 +106,20 @@ pub fn configure_routes(cfg: &mut web::ServiceConfig) { .route("/{id}/transaction", web::post().to(AssetController::add_transaction)) .route("/{id}/status/{status}", web::post().to(AssetController::update_status)) ) + + // Marketplace routes + .service( + web::scope("/marketplace") + .route("", web::get().to(MarketplaceController::index)) + .route("/listings", web::get().to(MarketplaceController::list_listings)) + .route("/my", web::get().to(MarketplaceController::my_listings)) + .route("/create", web::get().to(MarketplaceController::create_listing_form)) + .route("/create", web::post().to(MarketplaceController::create_listing)) + .route("/{id}", web::get().to(MarketplaceController::listing_detail)) + .route("/{id}/bid", web::post().to(MarketplaceController::submit_bid)) + .route("/{id}/purchase", web::post().to(MarketplaceController::purchase_listing)) + .route("/{id}/cancel", web::post().to(MarketplaceController::cancel_listing)) + ) ); // Keep the /protected scope for any future routes that should be under that path diff --git a/actix_mvc_app/src/views/base.html b/actix_mvc_app/src/views/base.html index 689e87e..9eeb986 100644 --- a/actix_mvc_app/src/views/base.html +++ b/actix_mvc_app/src/views/base.html @@ -91,6 +91,7 @@
  • New Ticket
  • My Tickets
  • My Assets
  • +
  • My Listings
  • My Votes
  • {% if user.role == "Admin" %}
  • Admin Panel
  • @@ -149,6 +150,11 @@ Digital Assets + +
    +
    + + Asset Preview +
    +
    +
    +
    + +
    +

    Select an asset to preview

    +
    +
    +
    + + +
    +
    + + Listing Tips +
    +
    +
      +
    • + + Use a clear, descriptive title +
    • +
    • + + Include detailed information about your asset +
    • +
    • + + Set a competitive price +
    • +
    • + + Add relevant tags to improve discoverability +
    • +
    • + + Choose the right listing type for your asset +
    • +
    +
    +
    + + + +{% endblock %} + +{% block scripts %} + +{% endblock %} diff --git a/actix_mvc_app/src/views/marketplace/index.html b/actix_mvc_app/src/views/marketplace/index.html new file mode 100644 index 0000000..d83f392 --- /dev/null +++ b/actix_mvc_app/src/views/marketplace/index.html @@ -0,0 +1,293 @@ +{% extends "base.html" %} + +{% block title %}Digital Assets Marketplace{% endblock %} + +{% block content %} +
    +

    Digital Assets Marketplace

    + + + +
    +
    +
    +
    +

    {{ stats.active_listings }}

    +

    Active Listings

    +
    + +
    +
    +
    +
    +
    +

    ${{ stats.total_value }}

    +

    Total Market Value

    +
    + +
    +
    +
    +
    +
    +

    {{ stats.total_listings }}

    +

    Total Listings

    +
    + +
    +
    +
    +
    +
    +

    ${{ stats.total_sales }}

    +

    Total Sales

    +
    + +
    +
    +
    + + + + + +
    +
    +
    +
    + + Featured Listings +
    +
    +
    + {% if featured_listings|length > 0 %} + {% for listing in featured_listings %} +
    +
    +
    Featured
    + {% if listing.image_url %} + {{ listing.title }} + {% else %} +
    + +
    + {% endif %} +
    +
    {{ listing.title }}
    +

    {{ listing.description }}

    +
    + {{ listing.listing_type.as_str() }} + {{ listing.asset_type.as_str() }} +
    +
    + +
    +
    + {% endfor %} + {% else %} +
    +

    No featured listings available at this time.

    +
    + {% endif %} +
    +
    +
    +
    +
    + + +
    + +
    +
    +
    + + Recent Listings +
    +
    +
    + + + + + + + + + + + + + + {% if recent_listings|length > 0 %} + {% for listing in recent_listings %} + + + + + + + + + + {% endfor %} + {% else %} + + + + {% endif %} + +
    AssetTypePriceListing TypeSellerListedAction
    +
    + {% if listing.image_url %} + {{ listing.asset_name }} + {% else %} + + {% endif %} + {{ listing.asset_name }} +
    +
    + {% if listing.asset_type.as_str() == "Token" %} + {{ listing.asset_type.as_str() }} + {% elif listing.asset_type.as_str() == "NFT" %} + {{ listing.asset_type.as_str() }} + {% elif listing.asset_type.as_str() == "RealEstate" %} + Real Estate + {% elif listing.asset_type.as_str() == "IntellectualProperty" %} + IP + {% else %} + {{ listing.asset_type.as_str() }} + {% endif %} + ${{ listing.price }}{{ listing.listing_type.as_str() }}{{ listing.seller_name }}{{ listing.created_at|date }} + View +
    No recent listings available.
    +
    +
    + +
    +
    + + +
    +
    +
    + + Recent Sales +
    +
    +
    + + + + + + + + + + {% if recent_sales|length > 0 %} + {% for listing in recent_sales %} + + + + + + {% endfor %} + {% else %} + + + + {% endif %} + +
    AssetPriceDate
    +
    + {% if listing.image_url %} + {{ listing.asset_name }} + {% else %} + + {% endif %} + {{ listing.asset_name }} +
    +
    ${{ listing.sale_price }}{{ listing.sold_at|date }}
    No recent sales available.
    +
    +
    +
    + + +
    +
    + + Listing Types +
    +
    +
    + + + + + + + + + {% for type, count in stats.listings_by_type %} + + + + + {% endfor %} + +
    TypeCount
    {{ type }}{{ count }}
    +
    +
    +
    +
    +
    +
    +{% endblock %} diff --git a/actix_mvc_app/src/views/marketplace/listing_detail.html b/actix_mvc_app/src/views/marketplace/listing_detail.html new file mode 100644 index 0000000..39d1d44 --- /dev/null +++ b/actix_mvc_app/src/views/marketplace/listing_detail.html @@ -0,0 +1,350 @@ +{% extends "base.html" %} + +{% block title %}{{ listing.title }} | Marketplace{% endblock %} + +{% block content %} +
    +

    Listing Details

    + + + +
    + +
    +
    +
    + {% if listing.image_url %} + {{ listing.title }} + {% else %} +
    + +
    + {% endif %} + +
    + {% if listing.listing_type.as_str() == "Fixed Price" %} + + {% elif listing.listing_type.as_str() == "Auction" %} + + {% elif listing.listing_type.as_str() == "Exchange" %} + + {% endif %} + + {% if listing.seller_id == user_id %} + + {% endif %} +
    +
    +
    + + +
    +
    + + Asset Information +
    +
    +

    Asset Name: {{ listing.asset_name }}

    +

    Asset Type: + {% if listing.asset_type.as_str() == "Token" %} + {{ listing.asset_type.as_str() }} + {% elif listing.asset_type.as_str() == "NFT" %} + {{ listing.asset_type.as_str() }} + {% elif listing.asset_type.as_str() == "RealEstate" %} + Real Estate + {% elif listing.asset_type.as_str() == "IntellectualProperty" %} + Intellectual Property + {% else %} + {{ listing.asset_type.as_str() }} + {% endif %} +

    +

    Asset ID: {{ listing.asset_id }}

    + + View Asset Details + +
    +
    +
    + + +
    +
    +
    +
    +
    + + Listing Details +
    +
    + + {{ listing.status.as_str() }} + +
    +
    +
    +
    +

    {{ listing.title }}

    + +
    +
    + {{ listing.listing_type.as_str() }} + {% if listing.featured %} + Featured + {% endif %} +
    +
    +

    ${{ listing.price }}

    +
    +
    + +

    {{ listing.description }}

    + +
    + +
    +
    +

    Seller: {{ listing.seller_name }}

    +

    Listed: {{ listing.created_at|date }}

    +
    +
    +

    Currency: {{ listing.currency }}

    +

    Expires: + {% if listing.expires_at %} + {{ listing.expires_at|date }} + {% else %} + No expiration + {% endif %} +

    +
    +
    + + {% if listing.tags|length > 0 %} +
    + Tags: + {% for tag in listing.tags %} + {{ tag }} + {% endfor %} +
    + {% endif %} +
    +
    + + + {% if listing.listing_type.as_str() == "Auction" %} +
    +
    + + Bids +
    +
    + {% if listing.bids|length > 0 %} +
    + + + + + + + + + + + {% for bid in listing.bids %} + + + + + + + {% endfor %} + +
    BidderAmountTimeStatus
    {{ bid.bidder_name }}${{ bid.amount }}{{ bid.created_at|date }} + + {{ bid.status.as_str() }} + +
    +
    +

    Current Highest Bid: ${{ listing.highest_bid().amount }}

    + {% else %} +

    No bids yet. Be the first to bid!

    +

    Starting Price: ${{ listing.price }}

    + {% endif %} +
    +
    + {% endif %} +
    +
    + + +
    +
    +
    +
    + + Similar Listings +
    +
    +
    + {% if similar_listings|length > 0 %} + {% for similar in similar_listings %} +
    +
    + {% if similar.image_url %} + {{ similar.title }} + {% else %} +
    + +
    + {% endif %} +
    +
    {{ similar.title }}
    +
    + {{ similar.listing_type.as_str() }} + {{ similar.asset_type.as_str() }} +
    +
    + +
    +
    + {% endfor %} + {% else %} +
    +

    No similar listings found.

    +
    + {% endif %} +
    +
    +
    +
    +
    +
    + + + + + + + + + +{% endblock %} diff --git a/actix_mvc_app/src/views/marketplace/listings.html b/actix_mvc_app/src/views/marketplace/listings.html new file mode 100644 index 0000000..aa50e32 --- /dev/null +++ b/actix_mvc_app/src/views/marketplace/listings.html @@ -0,0 +1,294 @@ +{% extends "base.html" %} + +{% block title %}Marketplace Listings{% endblock %} + +{% block content %} +
    +

    Marketplace Listings

    + + + +
    +
    + + Filter Listings +
    +
    +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    +
    +
    + + +
    +
    + + +
    + + List New Asset + +
    + + +
    +
    + {% if listings|length > 0 %} + {% for listing in listings %} +
    +
    + {% if listing.featured %} +
    Featured
    + {% endif %} + {% if listing.image_url %} + {{ listing.title }} + {% else %} +
    + +
    + {% endif %} +
    +
    {{ listing.title }}
    +

    {{ listing.description }}

    +
    + {{ listing.listing_type.as_str() }} + {% if listing.asset_type.as_str() == "Token" %} + {{ listing.asset_type.as_str() }} + {% elif listing.asset_type.as_str() == "NFT" %} + {{ listing.asset_type.as_str() }} + {% elif listing.asset_type.as_str() == "RealEstate" %} + Real Estate + {% elif listing.asset_type.as_str() == "IntellectualProperty" %} + IP + {% else %} + {{ listing.asset_type.as_str() }} + {% endif %} +
    +
    + Listed by {{ listing.seller_name }} +
    +
    + +
    +
    + {% endfor %} + {% else %} +
    +
    + No listings found. Create a new listing +
    +
    + {% endif %} +
    +
    + + + +
    + +{% endblock %} + +{% block scripts %} + +{% endblock %} diff --git a/actix_mvc_app/src/views/marketplace/my_listings.html b/actix_mvc_app/src/views/marketplace/my_listings.html new file mode 100644 index 0000000..6ff99bd --- /dev/null +++ b/actix_mvc_app/src/views/marketplace/my_listings.html @@ -0,0 +1,238 @@ +{% extends "base.html" %} + +{% block title %}My Marketplace Listings{% endblock %} + +{% block content %} +
    +

    My Listings

    + + + + + +
    +
    + + My Listings +
    +
    +
    + + + + + + + + + + + + + + + + {% if listings|length > 0 %} + {% for listing in listings %} + + + + + + + + + + + + {% endfor %} + {% else %} + + + + {% endif %} + +
    AssetTitlePriceTypeStatusCreatedExpiresViewsActions
    +
    + {% if listing.image_url %} + {{ listing.asset_name }} + {% else %} + + {% endif %} + {{ listing.asset_name }} +
    +
    {{ listing.title }}${{ listing.price }} + {{ listing.listing_type.as_str() }} + + {% if listing.status.as_str() == "Active" %} + {{ listing.status.as_str() }} + {% elif listing.status.as_str() == "Sold" %} + {{ listing.status.as_str() }} + {% elif listing.status.as_str() == "Cancelled" %} + {{ listing.status.as_str() }} + {% elif listing.status.as_str() == "Expired" %} + {{ listing.status.as_str() }} + {% endif %} + {{ listing.created_at|date }} + {% if listing.expires_at %} + {{ listing.expires_at|date }} + {% else %} + N/A + {% endif %} + {{ listing.views }} +
    + + + + {% if listing.status.as_str() == "Active" %} +
    + +
    + {% endif %} +
    +
    + You don't have any listings yet. + Create your first listing +
    +
    +
    +
    + + +
    +
    +
    +
    + + Listings by Status +
    +
    + +
    +
    +
    +
    +
    +
    + + Listings by Type +
    +
    + +
    +
    +
    +
    +
    +{% endblock %} + +{% block scripts %} + + +{% endblock %} From 19f8700b78f324861df7eb49ab6c315eb5746d6f Mon Sep 17 00:00:00 2001 From: Timur Gordon <31495328+timurgordon@users.noreply.github.com> Date: Tue, 29 Apr 2025 01:11:51 +0200 Subject: [PATCH 02/12] feat: Implement comprehensive DeFi platform in Digital Assets dashboard Add a complete DeFi platform with the following features: - Tabbed interface for different DeFi functionalities - Lending & Borrowing system with APY calculations - Liquidity Pools with LP token rewards - Staking options for tokens and digital assets - Token Swap interface with real-time exchange rates - Collateralization system for loans and synthetic assets - Interactive JavaScript functionality for real-time calculations This enhancement provides users with a complete suite of DeFi tools directly integrated into the Digital Assets dashboard. --- actix_mvc_app/src/views/assets/index.html | 1508 +++++++++++++++++++++ actix_mvc_app/static/js/defi.js | 571 ++++++++ 2 files changed, 2079 insertions(+) create mode 100644 actix_mvc_app/static/js/defi.js diff --git a/actix_mvc_app/src/views/assets/index.html b/actix_mvc_app/src/views/assets/index.html index e4b71f8..8fcbe01 100644 --- a/actix_mvc_app/src/views/assets/index.html +++ b/actix_mvc_app/src/views/assets/index.html @@ -62,6 +62,1511 @@ + +
    +
    + + DeFi Platform +
    +
    + + +
    + +
    +
    +

    Welcome to the ZAZ DeFi Platform!

    +

    Our decentralized finance platform allows you to maximize the value of your digital assets through various financial services.

    +
    +

    Use the tabs above to explore lending, borrowing, liquidity pools, staking, swapping, and collateralization features.

    +
    +
    + + +
    +
    +
    +
    +
    + Lend Your Assets +
    +
    +

    Earn interest by lending your digital assets to the ZAZ DeFi platform.

    + +
    +
    + + +
    + +
    + +
    + + TFT +
    +
    + +
    + + +
    + +
    +
    + Estimated Interest: + 0.00 TFT +
    +
    + Return Amount: + 0.00 TFT +
    +
    + +
    + +
    +
    +
    +
    +
    + +
    +
    +
    + Borrow Against Assets +
    +
    +

    Borrow digital assets using your existing assets as collateral.

    + +
    +
    + + +
    + +
    + + +
    + +
    + +
    + + TFT +
    +
    You can borrow up to 70% of your collateral value.
    +
    + +
    + + +
    + +
    +
    + Collateral Ratio: + 0% +
    +
    + Interest Due: + 0.00 TFT +
    +
    + Total Repayment: + 0.00 TFT +
    +
    +
    +
    +
    + +
    + +
    +
    +
    +
    +
    +
    + + +
    +
    + Your Active Positions +
    +
    + + +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    AssetAmountInterest RateStart DateEnd DateEarned InterestStatusActions
    ThreeFold Token (TFT)1,000 TFT4.2% APY2025-04-012025-05-013.5 TFTActive + +
    Zanzibar Token (ZAZ)500 ZAZ6.8% APY2025-03-152025-06-158.5 ZAZActive + +
    +
    +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    Borrowed AssetAmountCollateralCollateral RatioInterest RateStart DateDue DateActions
    ThreeFold Token (TFT)500 TFTBeach Property NFT +
    +
    +
    +
    + 65% +
    +
    5.2% APR2025-04-102025-05-10 + +
    +
    +
    +
    +
    +
    +
    + + +
    +
    +
    +
    +
    About Liquidity Pools
    +

    Liquidity pools are collections of tokens locked in smart contracts that provide liquidity for decentralized trading. By adding your assets to a liquidity pool, you earn a share of the trading fees generated by the pool.

    +
    +
    +
    + + +
    +
    +
    +
    + Available Liquidity Pools +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    PoolTotal Liquidity24h VolumeAPYYour LiquidityYour ShareActions
    +
    +
    + TFT + ZAZ +
    + TFT-ZAZ +
    +
    $1,250,000$45,00012.5%$2,5000.2% +
    + + +
    +
    +
    +
    + TFT + USDT +
    + TFT-USDT +
    +
    $3,750,000$125,0008.2%$00% +
    + + +
    +
    +
    +
    + ZAZ + USDT +
    + ZAZ-USDT +
    +
    $850,000$32,00015.8%$5,0000.59% +
    + + +
    +
    +
    +
    + BTC + USDT +
    + BTC-USDT +
    +
    $5,250,000$450,0006.5%$00% +
    + + +
    +
    +
    +
    + ETH + USDT +
    + ETH-USDT +
    +
    $4,100,000$320,0007.2%$00% +
    + + +
    +
    +
    +
    +
    +
    +
    + + +
    +
    +
    +
    + Your Liquidity Positions +
    +
    +
    + +
    +
    +
    +
    +
    + TFT + ZAZ +
    + TFT-ZAZ +
    +
    +
    +
    + Your Liquidity: + $2,500 +
    +
    + Pool Share: + 0.2% +
    +
    + TFT: + 500 TFT +
    +
    + ZAZ: + 1,250 ZAZ +
    +
    + Earned Fees: + $45.20 +
    +
    + APY: + 12.5% +
    +
    + + + +
    +
    +
    +
    + + +
    +
    +
    +
    +
    + ZAZ + USDT +
    + ZAZ-USDT +
    +
    +
    +
    + Your Liquidity: + $5,000 +
    +
    + Pool Share: + 0.59% +
    +
    + ZAZ: + 2,500 ZAZ +
    +
    + USDT: + 2,500 USDT +
    +
    + Earned Fees: + $128.75 +
    +
    + APY: + 15.8% +
    +
    + + + +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +
    +
    +
    +
    + Create New Liquidity Pool +
    +
    +
    +
    +
    +
    + + +
    +
    + +
    + + TFT +
    +
    +
    +
    +
    + + +
    +
    + +
    + + ZAZ +
    +
    +
    +
    +
    + +
    + 1 + TFT + = + + ZAZ +
    +
    +
    + + +
    This fee is charged on each trade and distributed to liquidity providers.
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    + + +
    +
    +
    +
    +
    About Swapping
    +

    Swap allows you to exchange one token for another at the current market rate. Swaps are executed through liquidity pools with a small fee that goes to liquidity providers.

    +
    +
    +
    + +
    +
    + +
    +
    + Swap Tokens +
    +
    + +
    + + +
    + + +
    + +
    + + +
    + +
    +
    + +
    + Balance: 5,000 ZAZ + ≈ $2,500.00 +
    +
    +
    +
    + + +
    +
    +
    + Exchange Rate: + 1 TFT = 0.5 ZAZ +
    +
    + Minimum Received: + 0 ZAZ +
    +
    + Price Impact: + < 0.1% +
    +
    + Liquidity Provider Fee: + 0.3% +
    +
    +
    + + +
    + +
    +
    +
    +
    + +
    + +
    +
    + Recent Swaps +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    TimeFromToValue
    2025-04-15 14:32 +
    + TFT + 500 TFT +
    +
    +
    + ZAZ + 250 ZAZ +
    +
    $250.00
    2025-04-14 09:17 +
    + USDT + 1,000 USDT +
    +
    +
    + TFT + 2,000 TFT +
    +
    $1,000.00
    2025-04-12 16:45 +
    + ZAZ + 100 ZAZ +
    +
    +
    + USDT + 50 USDT +
    +
    $50.00
    +
    +
    +
    + + +
    +
    + Market Rates +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    PairRate24h ChangeVolume (24h)
    +
    +
    + TFT + ZAZ +
    + TFT/ZAZ +
    +
    0.5+2.3%$125,000
    +
    +
    + TFT + USDT +
    + TFT/USDT +
    +
    0.5-1.2%$250,000
    +
    +
    + ZAZ + USDT +
    + ZAZ/USDT +
    +
    0.5+3.7%$175,000
    +
    +
    +
    +
    +
    +
    + + +
    +
    +
    +
    +
    About Collateralization
    +

    Use your digital assets as collateral to secure loans or generate synthetic assets. Maintain a healthy collateral ratio to avoid liquidation.

    +
    +
    +
    + +
    +
    + +
    +
    + Collateralize Assets +
    +
    +
    + +
    + + +
    + + +
    + +
    + + TFT +
    +
    + Available: 10,000 TFT ($5,000) +
    +
    + + +
    + +
    + $ + +
    +
    + + +
    + + +
    + + +
    + + +
    + + +
    + +
    + $ + + +
    +
    + Maximum Loan: $0.00 +
    +
    + + + + + + + + +
    + +
    + + + + +
    +
    + + +
    + +
    + $ + + per TFT +
    +
    + Your collateral will be liquidated if the price falls below this level. +
    +
    + + +
    + +
    +
    +
    +
    +
    + +
    + +
    +
    + Your Active Collateral Positions +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    AssetCollateral ValueBorrowed/GeneratedCollateral RatioLiquidation PriceStatusActions
    +
    + TFT + 2,000 TFT +
    +
    $1,000$700 (Loan) +
    +
    +
    +
    + 143% +
    +
    $0.35Healthy +
    + + +
    +
    +
    + + Beach Property NFT +
    +
    $25,00010,000 sUSD +
    +
    +
    +
    + 250% +
    +
    $10,000Warning +
    + + +
    +
    +
    + ZAZ + 1,000 ZAZ +
    +
    $5000.1 sBTC +
    +
    +
    +
    + 333% +
    +
    $0.15Healthy +
    + + +
    +
    +
    +
    +
    + + +
    +
    + Collateral Health +
    +
    +
    +
    Overall Collateral Health
    +
    +
    60%
    +
    +
    + Health score represents the overall safety of your collateral positions. Higher is better. +
    +
    + +
    +
    +
    +
    +
    Total Collateral Value
    +

    $26,500

    +
    +
    +
    +
    +
    +
    +
    Total Borrowed/Generated
    +

    $11,150

    +
    +
    +
    +
    + +
    + Your Beach Property NFT collateral is close to the liquidation threshold. Consider adding more collateral or repaying part of your synthetic assets. +
    +
    +
    +
    +
    +
    + + +
    +
    +
    +
    +
    About Staking
    +

    Staking allows you to lock your digital assets for a period of time to support network operations and earn rewards. The longer you stake, the higher rewards you can earn.

    +
    +
    +
    + + +
    +
    +
    +
    + Available Staking Options +
    +
    +
    + +
    +
    +
    +
    + TFT +
    ThreeFold Token (TFT)
    +
    +
    +
    +
    + Total Staked: + 5,250,000 TFT +
    +
    + Your Stake: + 1,000 TFT +
    +
    + APY: + 8.5% +
    + +
    + + +
    + +
    + +
    + + TFT +
    +
    + +
    +
    + Estimated Rewards: + 0 TFT +
    +
    + +
    + +
    +
    +
    +
    + + +
    +
    +
    +
    + ZAZ +
    Zanzibar Token (ZAZ)
    +
    +
    +
    +
    + Total Staked: + 2,750,000 ZAZ +
    +
    + Your Stake: + 500 ZAZ +
    +
    + APY: + 12.0% +
    + +
    + + +
    + +
    + +
    + + ZAZ +
    +
    + +
    +
    + Estimated Rewards: + 0 ZAZ +
    +
    + +
    + +
    +
    +
    +
    + + +
    +
    +
    +
    + +
    Digital Asset Staking
    +
    +
    +
    +

    Stake your NFTs and other digital assets to earn passive income.

    + +
    + + +
    + +
    + + +
    + +
    +
    + Estimated Rewards: + $0.00 +
    +
    + Reward Token: + ZAZ +
    +
    + +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +
    +
    +
    +
    + Your Active Stakes +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    AssetAmountValueStart DateEnd DateAPYEarned RewardsStatusActions
    +
    + TFT + ThreeFold Token (TFT) +
    +
    1,000 TFT$5002025-03-152025-06-1510.2%22.5 TFTActive + +
    +
    + ZAZ + Zanzibar Token (ZAZ) +
    +
    500 ZAZ$2502025-04-012025-05-0112.0%5.0 ZAZActive + +
    +
    + + Beach Property NFT +
    +
    1 NFT$25,0002025-02-102026-02-1010.0%450 ZAZActive + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    @@ -192,3 +1697,6 @@
    {% endblock %} + + + diff --git a/actix_mvc_app/static/js/defi.js b/actix_mvc_app/static/js/defi.js new file mode 100644 index 0000000..6c7bdc8 --- /dev/null +++ b/actix_mvc_app/static/js/defi.js @@ -0,0 +1,571 @@ +// DeFi Platform JavaScript Functionality + +document.addEventListener('DOMContentLoaded', function() { + // Initialize tooltips + var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]')); + var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) { + return new bootstrap.Tooltip(tooltipTriggerEl); + }); + + // =============== LENDING & BORROWING TAB =============== + // Lending form calculations + const lendingAmountInput = document.getElementById('lendingAmount'); + const lendingAssetSelect = document.getElementById('lendingAsset'); + const lendingTermSelect = document.getElementById('lendingTerm'); + const estimatedReturnsElement = document.getElementById('estimatedReturns'); + const totalReturnElement = document.getElementById('totalReturn'); + + if (lendingAmountInput && lendingAssetSelect && lendingTermSelect) { + const calculateLendingReturns = () => { + const amount = parseFloat(lendingAmountInput.value) || 0; + const asset = lendingAssetSelect.value; + const termDays = parseInt(lendingTermSelect.value) || 30; + + // Get APY from the selected option's text + const selectedOption = lendingTermSelect.options[lendingTermSelect.selectedIndex]; + const apyMatch = selectedOption.text.match(/\((\d+\.\d+)%\)/); + const apy = apyMatch ? parseFloat(apyMatch[1]) / 100 : 0.05; // Default to 5% if not found + + // Calculate returns (simple interest for demonstration) + const returns = amount * apy * (termDays / 365); + const total = amount + returns; + + if (estimatedReturnsElement) { + estimatedReturnsElement.textContent = returns.toFixed(2) + ' ' + asset; + } + if (totalReturnElement) { + totalReturnElement.textContent = total.toFixed(2) + ' ' + asset; + } + }; + + lendingAmountInput.addEventListener('input', calculateLendingReturns); + lendingAssetSelect.addEventListener('change', calculateLendingReturns); + lendingTermSelect.addEventListener('change', calculateLendingReturns); + } + + // Borrowing form calculations + const borrowingAmountInput = document.getElementById('borrowingAmount'); + const borrowingAssetSelect = document.getElementById('borrowingAsset'); + const borrowingTermSelect = document.getElementById('borrowingTerm'); + const borrowingCollateralSelect = document.getElementById('collateralAsset'); + const borrowingCollateralAmountInput = document.getElementById('collateralAmount'); + const interestDueElement = document.getElementById('interestDue'); + const totalRepaymentElement = document.getElementById('totalRepayment'); + const borrowingCollateralRatioElement = document.getElementById('collateralRatio'); + + if (borrowingAmountInput && borrowingAssetSelect && borrowingCollateralSelect && borrowingCollateralAmountInput) { + const calculateBorrowingDetails = () => { + const amount = parseFloat(borrowingAmountInput.value) || 0; + const asset = borrowingAssetSelect.value; + const termDays = parseInt(borrowingTermSelect.value) || 30; + + // Get APR from the selected option's text + const selectedOption = borrowingTermSelect.options[borrowingTermSelect.selectedIndex]; + const aprMatch = selectedOption.text.match(/\((\d+\.\d+)%\)/); + const apr = aprMatch ? parseFloat(aprMatch[1]) / 100 : 0.08; // Default to 8% if not found + + // Calculate interest and total repayment + const interest = amount * apr * (termDays / 365); + const total = amount + interest; + + if (interestDueElement) { + interestDueElement.textContent = interest.toFixed(2) + ' ' + asset; + } + if (totalRepaymentElement) { + totalRepaymentElement.textContent = total.toFixed(2) + ' ' + asset; + } + + // Calculate collateral ratio + const collateralAmount = parseFloat(borrowingCollateralAmountInput.value) || 0; + const collateralAsset = borrowingCollateralSelect.value; + let collateralValue = 0; + + // Mock prices for demonstration + const assetPrices = { + 'TFT': 0.5, + 'ZAZ': 0.5, + 'USDT': 1.0 + }; + + if (collateralAsset in assetPrices) { + collateralValue = collateralAmount * assetPrices[collateralAsset]; + } else { + // For other assets, assume the value is the amount (simplified) + collateralValue = collateralAmount; + } + + const borrowValue = amount * (asset === 'USDT' ? 1 : assetPrices[asset] || 0.5); + const ratio = borrowValue > 0 ? (collateralValue / borrowValue * 100) : 0; + + if (borrowingCollateralRatioElement) { + borrowingCollateralRatioElement.textContent = ratio.toFixed(0) + '%'; + + // Update color based on ratio + if (ratio >= 200) { + borrowingCollateralRatioElement.className = 'text-success'; + } else if (ratio >= 150) { + borrowingCollateralRatioElement.className = 'text-warning'; + } else { + borrowingCollateralRatioElement.className = 'text-danger'; + } + } + }; + + borrowingAmountInput.addEventListener('input', calculateBorrowingDetails); + borrowingAssetSelect.addEventListener('change', calculateBorrowingDetails); + borrowingTermSelect.addEventListener('change', calculateBorrowingDetails); + borrowingCollateralSelect.addEventListener('change', calculateBorrowingDetails); + borrowingCollateralAmountInput.addEventListener('input', calculateBorrowingDetails); + } + + // =============== LIQUIDITY POOLS TAB =============== + // Add Liquidity form calculations + const poolSelect = document.getElementById('liquidityPool'); + const token1AmountInput = document.getElementById('token1Amount'); + const token2AmountInput = document.getElementById('token2Amount'); + const lpTokensElement = document.getElementById('lpTokensReceived'); + const poolShareElement = document.getElementById('poolShare'); + + if (poolSelect && token1AmountInput && token2AmountInput) { + const calculateLiquidityDetails = () => { + const token1Amount = parseFloat(token1AmountInput.value) || 0; + const token2Amount = parseFloat(token2AmountInput.value) || 0; + + // Mock calculations for demonstration + const lpTokens = Math.sqrt(token1Amount * token2Amount); + const poolShare = token1Amount > 0 ? (lpTokens / (lpTokens + 1000) * 100) : 0; + + if (lpTokensElement) { + lpTokensElement.textContent = lpTokens.toFixed(2); + } + if (poolShareElement) { + poolShareElement.textContent = poolShare.toFixed(2) + '%'; + } + }; + + token1AmountInput.addEventListener('input', calculateLiquidityDetails); + token2AmountInput.addEventListener('input', calculateLiquidityDetails); + + // Handle pool selection to update token labels + poolSelect.addEventListener('change', function() { + const selectedOption = poolSelect.options[poolSelect.selectedIndex]; + const token1Label = document.getElementById('token1Label'); + const token2Label = document.getElementById('token2Label'); + + if (selectedOption.value === 'tft-zaz') { + if (token1Label) token1Label.textContent = 'TFT'; + if (token2Label) token2Label.textContent = 'ZAZ'; + } else if (selectedOption.value === 'zaz-usdt') { + if (token1Label) token1Label.textContent = 'ZAZ'; + if (token2Label) token2Label.textContent = 'USDT'; + } + + calculateLiquidityDetails(); + }); + } + + // =============== STAKING TAB =============== + // TFT Staking calculations + const tftStakeAmountInput = document.getElementById('tftStakeAmount'); + const tftStakingPeriodSelect = document.getElementById('tftStakingPeriod'); + const tftEstimatedRewardsElement = document.getElementById('tftEstimatedRewards'); + + if (tftStakeAmountInput && tftStakingPeriodSelect && tftEstimatedRewardsElement) { + const calculateTftStakingRewards = () => { + const amount = parseFloat(tftStakeAmountInput.value) || 0; + const termDays = parseInt(tftStakingPeriodSelect.value) || 30; + + // Get APY from the selected option's text + const selectedOption = tftStakingPeriodSelect.options[tftStakingPeriodSelect.selectedIndex]; + const apyMatch = selectedOption.text.match(/\((\d+\.\d+)%\)/); + const apy = apyMatch ? parseFloat(apyMatch[1]) / 100 : 0.085; // Default to 8.5% if not found + + // Calculate rewards (simple interest for demonstration) + const rewards = amount * apy * (termDays / 365); + + tftEstimatedRewardsElement.textContent = rewards.toFixed(2) + ' TFT'; + }; + + tftStakeAmountInput.addEventListener('input', calculateTftStakingRewards); + tftStakingPeriodSelect.addEventListener('change', calculateTftStakingRewards); + } + + // ZAZ Staking calculations + const zazStakeAmountInput = document.getElementById('zazStakeAmount'); + const zazStakingPeriodSelect = document.getElementById('zazStakingPeriod'); + const zazEstimatedRewardsElement = document.getElementById('zazEstimatedRewards'); + + if (zazStakeAmountInput && zazStakingPeriodSelect && zazEstimatedRewardsElement) { + const calculateZazStakingRewards = () => { + const amount = parseFloat(zazStakeAmountInput.value) || 0; + const termDays = parseInt(zazStakingPeriodSelect.value) || 30; + + // Get APY from the selected option's text + const selectedOption = zazStakingPeriodSelect.options[zazStakingPeriodSelect.selectedIndex]; + const apyMatch = selectedOption.text.match(/\((\d+\.\d+)%\)/); + const apy = apyMatch ? parseFloat(apyMatch[1]) / 100 : 0.12; // Default to 12% if not found + + // Calculate rewards (simple interest for demonstration) + const rewards = amount * apy * (termDays / 365); + + zazEstimatedRewardsElement.textContent = rewards.toFixed(2) + ' ZAZ'; + }; + + zazStakeAmountInput.addEventListener('input', calculateZazStakingRewards); + zazStakingPeriodSelect.addEventListener('change', calculateZazStakingRewards); + } + + // Asset Staking calculations + const assetStakingSelect = document.getElementById('assetStaking'); + const assetStakingPeriodSelect = document.getElementById('assetStakingPeriod'); + const assetEstimatedRewardsElement = document.getElementById('assetEstimatedRewards'); + + if (assetStakingSelect && assetStakingPeriodSelect && assetEstimatedRewardsElement) { + const calculateAssetStakingRewards = () => { + const selectedOption = assetStakingSelect.options[assetStakingSelect.selectedIndex]; + if (selectedOption.value === '') return; + + const assetValue = parseFloat(selectedOption.dataset.value) || 0; + const termDays = parseInt(assetStakingPeriodSelect.value) || 30; + + // Get APY from the selected option's text + const periodOption = assetStakingPeriodSelect.options[assetStakingPeriodSelect.selectedIndex]; + const apyMatch = periodOption.text.match(/\((\d+\.\d+)%\)/); + const apy = apyMatch ? parseFloat(apyMatch[1]) / 100 : 0.035; // Default to 3.5% if not found + + // Calculate rewards in USD (simple interest for demonstration) + const rewards = assetValue * apy * (termDays / 365); + + assetEstimatedRewardsElement.textContent = '$' + rewards.toFixed(2); + }; + + assetStakingSelect.addEventListener('change', calculateAssetStakingRewards); + assetStakingPeriodSelect.addEventListener('change', calculateAssetStakingRewards); + } + + // =============== SWAP TAB =============== + // Token swap calculations + const swapFromAmountInput = document.getElementById('swapFromAmount'); + const swapToAmountElement = document.getElementById('swapToAmount'); + const fromTokenDropdown = document.getElementById('fromTokenDropdown'); + const toTokenDropdown = document.getElementById('toTokenDropdown'); + const exchangeRateElement = document.getElementById('exchangeRate'); + const minimumReceivedElement = document.getElementById('minimumReceived'); + const priceImpactElement = document.getElementById('priceImpact'); + const swapDirectionButton = document.getElementById('swapDirectionButton'); + const maxFromButton = document.getElementById('maxFromButton'); + const fromTokenSymbolElement = document.getElementById('fromTokenSymbol'); + const toTokenSymbolElement = document.getElementById('toTokenSymbol'); + const fromTokenImgElement = document.getElementById('fromTokenImg'); + const toTokenImgElement = document.getElementById('toTokenImg'); + const fromTokenBalanceElement = document.getElementById('fromTokenBalance'); + const toTokenBalanceElement = document.getElementById('toTokenBalance'); + + // Mock token data + const tokenData = { + 'TFT': { price: 0.5, balance: '10,000 TFT', usdValue: '5,000.00' }, + 'ZAZ': { price: 0.5, balance: '5,000 ZAZ', usdValue: '2,500.00' }, + 'USDT': { price: 1.0, balance: '2,500 USDT', usdValue: '2,500.00' } + }; + + if (swapFromAmountInput && swapToAmountElement) { + let fromToken = 'TFT'; + let toToken = 'ZAZ'; + + const calculateSwap = () => { + const fromAmount = parseFloat(swapFromAmountInput.value) || 0; + + // Calculate exchange rate + const fromPrice = tokenData[fromToken].price; + const toPrice = tokenData[toToken].price; + const rate = fromPrice / toPrice; + + // Calculate to amount + const toAmount = fromAmount * rate; + + // Update UI + swapToAmountElement.value = toAmount.toFixed(2); + + if (exchangeRateElement) { + exchangeRateElement.textContent = `1 ${fromToken} = ${rate.toFixed(4)} ${toToken}`; + } + + if (minimumReceivedElement) { + // 0.5% slippage for demonstration + const minReceived = toAmount * 0.995; + minimumReceivedElement.textContent = `${minReceived.toFixed(2)} ${toToken}`; + } + + if (priceImpactElement) { + // Mock price impact calculation + const impact = fromAmount > 1000 ? '0.5%' : '< 0.1%'; + priceImpactElement.textContent = impact; + priceImpactElement.className = fromAmount > 1000 ? 'text-warning' : 'text-success'; + } + }; + + // Initialize from token dropdown items + const fromTokenItems = document.querySelectorAll('[aria-labelledby="fromTokenDropdown"] .dropdown-item'); + fromTokenItems.forEach(item => { + item.addEventListener('click', function(e) { + e.preventDefault(); + fromToken = this.dataset.token; + fromTokenSymbolElement.textContent = fromToken; + fromTokenImgElement.src = this.dataset.img; + fromTokenBalanceElement.textContent = tokenData[fromToken].balance; + calculateSwap(); + }); + }); + + // Initialize to token dropdown items + const toTokenItems = document.querySelectorAll('[aria-labelledby="toTokenDropdown"] .dropdown-item'); + toTokenItems.forEach(item => { + item.addEventListener('click', function(e) { + e.preventDefault(); + toToken = this.dataset.token; + toTokenSymbolElement.textContent = toToken; + toTokenImgElement.src = this.dataset.img; + toTokenBalanceElement.textContent = tokenData[toToken].balance; + calculateSwap(); + }); + }); + + // Swap direction button + if (swapDirectionButton) { + swapDirectionButton.addEventListener('click', function() { + // Swap tokens + const tempToken = fromToken; + fromToken = toToken; + toToken = tempToken; + + // Update UI + fromTokenSymbolElement.textContent = fromToken; + toTokenSymbolElement.textContent = toToken; + + const tempImg = fromTokenImgElement.src; + fromTokenImgElement.src = toTokenImgElement.src; + toTokenImgElement.src = tempImg; + + fromTokenBalanceElement.textContent = tokenData[fromToken].balance; + toTokenBalanceElement.textContent = tokenData[toToken].balance; + + // Swap amounts + const tempAmount = swapFromAmountInput.value; + swapFromAmountInput.value = swapToAmountElement.value; + + calculateSwap(); + }); + } + + // Max button + if (maxFromButton) { + maxFromButton.addEventListener('click', function() { + // Set max amount based on token balance + const balance = parseInt(tokenData[fromToken].balance.split(' ')[0].replace(/,/g, '')); + swapFromAmountInput.value = balance; + calculateSwap(); + }); + } + + swapFromAmountInput.addEventListener('input', calculateSwap); + + // Initial calculation + calculateSwap(); + } + + // =============== COLLATERAL TAB =============== + // Collateral form calculations + const collateralAssetSelect = document.getElementById('collateralAsset'); + const collateralAmountInput = document.getElementById('collateralAmount'); + const collateralValueElement = document.getElementById('collateralValue'); + const collateralUnitElement = document.getElementById('collateralUnit'); + const collateralAvailableElement = document.getElementById('collateralAvailable'); + const collateralAvailableUSDElement = document.getElementById('collateralAvailableUSD'); + const collateralPurposeSelect = document.getElementById('collateralPurpose'); + const loanTermGroup = document.getElementById('loanTermGroup'); + const loanAmountGroup = document.getElementById('loanAmountGroup'); + const syntheticAssetGroup = document.getElementById('syntheticAssetGroup'); + const syntheticAmountGroup = document.getElementById('syntheticAmountGroup'); + const loanAmountInput = document.getElementById('loanAmount'); + const maxLoanAmountElement = document.getElementById('maxLoanAmount'); + const syntheticAmountInput = document.getElementById('syntheticAmount'); + const maxSyntheticAmountElement = document.getElementById('maxSyntheticAmount'); + const collateralRatioElement = document.getElementById('collateralRatio'); + const liquidationPriceElement = document.getElementById('liquidationPrice'); + const liquidationUnitElement = document.getElementById('liquidationUnit'); + + if (collateralAssetSelect && collateralAmountInput) { + const calculateCollateralDetails = () => { + if (collateralAssetSelect.selectedIndex === 0) return; + + const selectedOption = collateralAssetSelect.options[collateralAssetSelect.selectedIndex]; + const assetType = selectedOption.dataset.type; + const assetValue = parseFloat(selectedOption.dataset.value) || 0; + const assetAmount = parseFloat(selectedOption.dataset.amount) || 0; + const assetUnit = selectedOption.dataset.unit || ''; + + // Update UI with asset details + if (collateralUnitElement) collateralUnitElement.textContent = assetUnit; + if (collateralAvailableElement) collateralAvailableElement.textContent = assetAmount.toLocaleString() + ' ' + assetUnit; + if (collateralAvailableUSDElement) collateralAvailableUSDElement.textContent = '$' + assetValue.toLocaleString(); + if (liquidationUnitElement) liquidationUnitElement.textContent = assetUnit; + + // Calculate collateral value + const amount = parseFloat(collateralAmountInput.value) || 0; + let collateralValue = 0; + + if (assetType === 'token') { + // For tokens, calculate based on token price + const tokenPrice = assetValue / assetAmount; + collateralValue = amount * tokenPrice; + } else { + // For NFTs and other assets, use the full value if amount is 1 + collateralValue = amount === 1 ? assetValue : 0; + } + + if (collateralValueElement) collateralValueElement.value = collateralValue.toFixed(2); + + // Calculate max loan amount (75% of collateral value) + const maxLoanAmount = collateralValue * 0.75; + if (maxLoanAmountElement) maxLoanAmountElement.textContent = maxLoanAmount.toFixed(2); + + // Calculate max synthetic amount (50% of collateral value) + const maxSyntheticAmount = collateralValue * 0.5; + if (maxSyntheticAmountElement) maxSyntheticAmountElement.textContent = maxSyntheticAmount.toFixed(2); + + // Calculate collateral ratio and liquidation price + updateCollateralRatio(); + }; + + const updateCollateralRatio = () => { + const collateralValue = parseFloat(collateralValueElement.value) || 0; + const purpose = collateralPurposeSelect.value; + + let borrowedValue = 0; + if (purpose === 'loan') { + borrowedValue = parseFloat(loanAmountInput.value) || 0; + } else if (purpose === 'synthetic') { + borrowedValue = parseFloat(syntheticAmountInput.value) || 0; + } else { + // For leverage trading, assume 2x leverage + borrowedValue = collateralValue; + } + + // Calculate ratio + const ratio = borrowedValue > 0 ? (collateralValue / borrowedValue * 100) : 0; + + if (collateralRatioElement) { + collateralRatioElement.value = ratio.toFixed(0) + '%'; + } + + // Calculate liquidation price + if (liquidationPriceElement) { + const selectedOption = collateralAssetSelect.options[collateralAssetSelect.selectedIndex]; + if (selectedOption.selectedIndex === 0) return; + + const assetType = selectedOption.dataset.type; + const assetValue = parseFloat(selectedOption.dataset.value) || 0; + const assetAmount = parseFloat(selectedOption.dataset.amount) || 0; + const collateralAmount = parseFloat(collateralAmountInput.value) || 0; + + if (assetType === 'token' && collateralAmount > 0) { + const currentPrice = assetValue / assetAmount; + const liquidationThreshold = purpose === 'loan' ? 1.2 : 1.5; // 120% for loans, 150% for synthetic + const liquidationPrice = (borrowedValue / collateralAmount) * liquidationThreshold; + liquidationPriceElement.value = liquidationPrice.toFixed(4); + } else { + liquidationPriceElement.value = (borrowedValue * 1.2).toFixed(2); + } + } + }; + + // Handle collateral asset selection + collateralAssetSelect.addEventListener('change', function() { + collateralAmountInput.value = ''; + calculateCollateralDetails(); + }); + + // Handle collateral amount input + collateralAmountInput.addEventListener('input', calculateCollateralDetails); + + // Handle purpose selection + collateralPurposeSelect.addEventListener('change', function() { + const purpose = collateralPurposeSelect.value; + + // Show/hide relevant form groups + if (loanTermGroup) loanTermGroup.style.display = purpose === 'loan' ? 'block' : 'none'; + if (loanAmountGroup) loanAmountGroup.style.display = purpose === 'loan' ? 'block' : 'none'; + if (syntheticAssetGroup) syntheticAssetGroup.style.display = purpose === 'synthetic' ? 'block' : 'none'; + if (syntheticAmountGroup) syntheticAmountGroup.style.display = purpose === 'synthetic' ? 'block' : 'none'; + + updateCollateralRatio(); + }); + + // Handle loan amount input + if (loanAmountInput) { + loanAmountInput.addEventListener('input', updateCollateralRatio); + + // Max loan button + const maxLoanButton = document.getElementById('maxLoanButton'); + if (maxLoanButton) { + maxLoanButton.addEventListener('click', function() { + const maxLoan = parseFloat(maxLoanAmountElement.textContent) || 0; + loanAmountInput.value = maxLoan.toFixed(2); + updateCollateralRatio(); + }); + } + } + + // Handle synthetic amount input + if (syntheticAmountInput) { + syntheticAmountInput.addEventListener('input', updateCollateralRatio); + + // Max synthetic button + const maxSyntheticButton = document.getElementById('maxSyntheticButton'); + if (maxSyntheticButton) { + maxSyntheticButton.addEventListener('click', function() { + const maxSynthetic = parseFloat(maxSyntheticAmountElement.textContent) || 0; + syntheticAmountInput.value = maxSynthetic.toFixed(2); + updateCollateralRatio(); + }); + } + } + + // Handle synthetic asset selection + const syntheticAssetSelect = document.getElementById('syntheticAsset'); + const syntheticUnitElement = document.getElementById('syntheticUnit'); + const maxSyntheticUnitElement = document.getElementById('maxSyntheticUnit'); + + if (syntheticAssetSelect && syntheticUnitElement && maxSyntheticUnitElement) { + syntheticAssetSelect.addEventListener('change', function() { + const asset = syntheticAssetSelect.value; + syntheticUnitElement.textContent = asset; + maxSyntheticUnitElement.textContent = asset; + }); + } + } + + // Initialize tab functionality if not already handled by Bootstrap + const tabLinks = document.querySelectorAll('.nav-link[data-bs-toggle="tab"]'); + tabLinks.forEach(tabLink => { + tabLink.addEventListener('click', function(e) { + e.preventDefault(); + const targetId = this.getAttribute('href'); + const targetTab = document.querySelector(targetId); + + // Hide all tabs + document.querySelectorAll('.tab-pane').forEach(tab => { + tab.classList.remove('show', 'active'); + }); + + // Show the target tab + if (targetTab) { + targetTab.classList.add('show', 'active'); + } + + // Update active state on nav links + tabLinks.forEach(link => link.classList.remove('active')); + this.classList.add('active'); + }); + }); +}); From 457f3c826856cf422d63dd8daa29f240ca63e90a Mon Sep 17 00:00:00 2001 From: despiegk Date: Tue, 29 Apr 2025 06:27:28 +0400 Subject: [PATCH 03/12] fixes --- actix_mvc_app/src/controllers/marketplace.rs | 49 +++++++++++++---- .../src/views/marketplace/create_listing.html | 4 +- .../src/views/marketplace/index.html | 20 +++---- .../src/views/marketplace/listing_detail.html | 52 +++++++++++-------- .../src/views/marketplace/listings.html | 42 +++++++-------- .../src/views/marketplace/my_listings.html | 20 +++---- 6 files changed, 112 insertions(+), 75 deletions(-) diff --git a/actix_mvc_app/src/controllers/marketplace.rs b/actix_mvc_app/src/controllers/marketplace.rs index ebe48d5..9ba00ce 100644 --- a/actix_mvc_app/src/controllers/marketplace.rs +++ b/actix_mvc_app/src/controllers/marketplace.rs @@ -103,7 +103,10 @@ impl MarketplaceController { AssetType::NFT.as_str(), AssetType::RealEstate.as_str(), AssetType::IntellectualProperty.as_str(), - AssetType::PhysicalAsset.as_str(), + AssetType::Commodity.as_str(), + AssetType::Share.as_str(), + AssetType::Bond.as_str(), + AssetType::Other.as_str(), ]); render_template(&tmpl, "marketplace/listings.html", &context) @@ -140,15 +143,28 @@ impl MarketplaceController { if let Some(listing) = listing { // Get similar listings (same asset type, active) let similar_listings: Vec<&Listing> = listings.iter() - .filter(|l| l.asset_type == listing.asset_type && - l.status == ListingStatus::Active && + .filter(|l| l.asset_type == listing.asset_type && + l.status == ListingStatus::Active && l.id != listing.id) .take(4) .collect(); + // Get highest bid amount and minimum bid for auction listings + let (highest_bid_amount, minimum_bid) = if listing.listing_type == ListingType::Auction { + if let Some(bid) = listing.highest_bid() { + (Some(bid.amount), bid.amount + 1.0) + } else { + (None, listing.price + 1.0) + } + } else { + (None, 0.0) + }; + context.insert("active_page", &"marketplace"); context.insert("listing", listing); context.insert("similar_listings", &similar_listings); + context.insert("highest_bid_amount", &highest_bid_amount); + context.insert("minimum_bid", &minimum_bid); // Add current user info for bid/purchase forms let user_id = "user-123"; @@ -311,7 +327,7 @@ impl MarketplaceController { tmpl: web::Data, path: web::Path, ) -> Result { - let listing_id = path.into_inner(); + let _listing_id = path.into_inner(); // In a real application, we would: // 1. Find the listing in the database @@ -344,7 +360,10 @@ impl MarketplaceController { AssetType::NFT => 500.0 + (i as f64 * 100.0), AssetType::RealEstate => 50000.0 + (i as f64 * 10000.0), AssetType::IntellectualProperty => 2000.0 + (i as f64 * 500.0), - AssetType::PhysicalAsset => 1000.0 + (i as f64 * 200.0), + AssetType::Commodity => 1000.0 + (i as f64 * 200.0), + AssetType::Share => 300.0 + (i as f64 * 50.0), + AssetType::Bond => 1500.0 + (i as f64 * 300.0), + AssetType::Other => 800.0 + (i as f64 * 150.0), }; let mut listing = Listing::new( @@ -382,7 +401,10 @@ impl MarketplaceController { AssetType::NFT => 400.0 + (i as f64 * 50.0), AssetType::RealEstate => 40000.0 + (i as f64 * 5000.0), AssetType::IntellectualProperty => 1500.0 + (i as f64 * 300.0), - AssetType::PhysicalAsset => 800.0 + (i as f64 * 100.0), + AssetType::Commodity => 800.0 + (i as f64 * 100.0), + AssetType::Share => 250.0 + (i as f64 * 40.0), + AssetType::Bond => 1200.0 + (i as f64 * 250.0), + AssetType::Other => 600.0 + (i as f64 * 120.0), }; let mut listing = Listing::new( @@ -435,7 +457,10 @@ impl MarketplaceController { AssetType::NFT => 600.0 + (i as f64 * 150.0), AssetType::RealEstate => 60000.0 + (i as f64 * 15000.0), AssetType::IntellectualProperty => 2500.0 + (i as f64 * 600.0), - AssetType::PhysicalAsset => 1200.0 + (i as f64 * 300.0), + AssetType::Commodity => 1200.0 + (i as f64 * 300.0), + AssetType::Share => 350.0 + (i as f64 * 70.0), + AssetType::Bond => 1800.0 + (i as f64 * 350.0), + AssetType::Other => 1000.0 + (i as f64 * 200.0), }; let listing = Listing::new( @@ -469,7 +494,10 @@ impl MarketplaceController { AssetType::NFT => 550.0 + (i as f64 * 120.0), AssetType::RealEstate => 55000.0 + (i as f64 * 12000.0), AssetType::IntellectualProperty => 2200.0 + (i as f64 * 550.0), - AssetType::PhysicalAsset => 1100.0 + (i as f64 * 220.0), + AssetType::Commodity => 1100.0 + (i as f64 * 220.0), + AssetType::Share => 320.0 + (i as f64 * 60.0), + AssetType::Bond => 1650.0 + (i as f64 * 330.0), + AssetType::Other => 900.0 + (i as f64 * 180.0), }; let sale_price = price * 0.95; // Slight discount on sale @@ -515,7 +543,10 @@ impl MarketplaceController { AssetType::NFT => 450.0 + (i as f64 * 80.0), AssetType::RealEstate => 45000.0 + (i as f64 * 8000.0), AssetType::IntellectualProperty => 1800.0 + (i as f64 * 400.0), - AssetType::PhysicalAsset => 900.0 + (i as f64 * 180.0), + AssetType::Commodity => 900.0 + (i as f64 * 180.0), + AssetType::Share => 280.0 + (i as f64 * 45.0), + AssetType::Bond => 1350.0 + (i as f64 * 270.0), + AssetType::Other => 750.0 + (i as f64 * 150.0), }; let mut listing = Listing::new( diff --git a/actix_mvc_app/src/views/marketplace/create_listing.html b/actix_mvc_app/src/views/marketplace/create_listing.html index 1725e7b..82e44a0 100644 --- a/actix_mvc_app/src/views/marketplace/create_listing.html +++ b/actix_mvc_app/src/views/marketplace/create_listing.html @@ -35,8 +35,8 @@ diff --git a/actix_mvc_app/src/views/marketplace/index.html b/actix_mvc_app/src/views/marketplace/index.html index d83f392..1eee45b 100644 --- a/actix_mvc_app/src/views/marketplace/index.html +++ b/actix_mvc_app/src/views/marketplace/index.html @@ -116,8 +116,8 @@
    {{ listing.title }}

    {{ listing.description }}

    - {{ listing.listing_type.as_str() }} - {{ listing.asset_type.as_str() }} + {{ listing.listing_type }} + {{ listing.asset_type }}
    - {% if listing.asset_type.as_str() == "Token" %} - {{ listing.asset_type.as_str() }} - {% elif listing.asset_type.as_str() == "NFT" %} - {{ listing.asset_type.as_str() }} - {% elif listing.asset_type.as_str() == "RealEstate" %} + {% if listing.asset_type == "Token" %} + {{ listing.asset_type }} + {% elif listing.asset_type == "NFT" %} + {{ listing.asset_type }} + {% elif listing.asset_type == "RealEstate" %} Real Estate - {% elif listing.asset_type.as_str() == "IntellectualProperty" %} + {% elif listing.asset_type == "IntellectualProperty" %} IP {% else %} - {{ listing.asset_type.as_str() }} + {{ listing.asset_type }} {% endif %} ${{ listing.price }} - {{ listing.listing_type.as_str() }} + {{ listing.listing_type }} {{ listing.seller_name }} {{ listing.created_at|date }} diff --git a/actix_mvc_app/src/views/marketplace/listing_detail.html b/actix_mvc_app/src/views/marketplace/listing_detail.html index 39d1d44..9daa872 100644 --- a/actix_mvc_app/src/views/marketplace/listing_detail.html +++ b/actix_mvc_app/src/views/marketplace/listing_detail.html @@ -27,15 +27,15 @@ {% endif %}
    - {% if listing.listing_type.as_str() == "Fixed Price" %} + {% if listing.listing_type == "Fixed Price" %} - {% elif listing.listing_type.as_str() == "Auction" %} + {% elif listing.listing_type == "Auction" %} - {% elif listing.listing_type.as_str() == "Exchange" %} + {% elif listing.listing_type == "Exchange" %} @@ -59,16 +59,16 @@

    Asset Name: {{ listing.asset_name }}

    Asset Type: - {% if listing.asset_type.as_str() == "Token" %} - {{ listing.asset_type.as_str() }} - {% elif listing.asset_type.as_str() == "NFT" %} - {{ listing.asset_type.as_str() }} - {% elif listing.asset_type.as_str() == "RealEstate" %} + {% if listing.asset_type == "Token" %} + {{ listing.asset_type }} + {% elif listing.asset_type == "NFT" %} + {{ listing.asset_type }} + {% elif listing.asset_type == "RealEstate" %} Real Estate - {% elif listing.asset_type.as_str() == "IntellectualProperty" %} + {% elif listing.asset_type == "IntellectualProperty" %} Intellectual Property {% else %} - {{ listing.asset_type.as_str() }} + {{ listing.asset_type }} {% endif %}

    Asset ID: {{ listing.asset_id }}

    @@ -89,8 +89,11 @@ Listing Details
    - - {{ listing.status.as_str() }} + {% if listing.status == 'Active' %} + {{ listing.status }} + {% else %} + {{ listing.status }} + {% endif %}
    @@ -100,7 +103,7 @@
    - {{ listing.listing_type.as_str() }} + {{ listing.listing_type }} {% if listing.featured %} Featured {% endif %} @@ -143,7 +146,7 @@
    - {% if listing.listing_type.as_str() == "Auction" %} + {% if listing.listing_type == "Auction" %}
    @@ -168,8 +171,11 @@ ${{ bid.amount }} {{ bid.created_at|date }} - - {{ bid.status.as_str() }} + {% if bid.status == 'Active' %} + {{ bid.status }} + {% else %} + {{ bid.status }} + {% endif %} @@ -177,7 +183,7 @@
    -

    Current Highest Bid: ${{ listing.highest_bid().amount }}

    +

    Current Highest Bid: ${{ listing.highest_bid_amount }}

    {% else %}

    No bids yet. Be the first to bid!

    Starting Price: ${{ listing.price }}

    @@ -212,8 +218,8 @@
    {{ similar.title }}
    - {{ similar.listing_type.as_str() }} - {{ similar.asset_type.as_str() }} + {{ similar.listing_type }} + {{ similar.asset_type }}
    diff --git a/actix_mvc_app/src/views/marketplace/listings.html b/actix_mvc_app/src/views/marketplace/listings.html index aa50e32..bfe9777 100644 --- a/actix_mvc_app/src/views/marketplace/listings.html +++ b/actix_mvc_app/src/views/marketplace/listings.html @@ -77,9 +77,9 @@
    {% if listings|length > 0 %} {% for listing in listings %} -
    {% if listing.featured %} @@ -96,17 +96,17 @@
    {{ listing.title }}

    {{ listing.description }}

    - {{ listing.listing_type.as_str() }} - {% if listing.asset_type.as_str() == "Token" %} - {{ listing.asset_type.as_str() }} - {% elif listing.asset_type.as_str() == "NFT" %} - {{ listing.asset_type.as_str() }} - {% elif listing.asset_type.as_str() == "RealEstate" %} + {{ listing.listing_type }} + {% if listing.asset_type == "Token" %} + {{ listing.asset_type }} + {% elif listing.asset_type == "NFT" %} + {{ listing.asset_type }} + {% elif listing.asset_type == "RealEstate" %} Real Estate - {% elif listing.asset_type.as_str() == "IntellectualProperty" %} + {% elif listing.asset_type == "IntellectualProperty" %} IP {% else %} - {{ listing.asset_type.as_str() }} + {{ listing.asset_type }} {% endif %}
    @@ -159,8 +159,8 @@ {% if listings|length > 0 %} {% for listing in listings %}
    @@ -173,20 +173,20 @@ {{ listing.title }} - {% if listing.asset_type.as_str() == "Token" %} - {{ listing.asset_type.as_str() }} - {% elif listing.asset_type.as_str() == "NFT" %} - {{ listing.asset_type.as_str() }} - {% elif listing.asset_type.as_str() == "RealEstate" %} + {% if listing.asset_type == "Token" %} + {{ listing.asset_type }} + {% elif listing.asset_type == "NFT" %} + {{ listing.asset_type }} + {% elif listing.asset_type == "RealEstate" %} Real Estate - {% elif listing.asset_type.as_str() == "IntellectualProperty" %} + {% elif listing.asset_type == "IntellectualProperty" %} IP {% else %} - {{ listing.asset_type.as_str() }} + {{ listing.asset_type }} {% endif %} ${{ listing.price }} - {{ listing.listing_type.as_str() }} + {{ listing.listing_type }} {{ listing.seller_name }} {{ listing.created_at|date }} diff --git a/actix_mvc_app/src/views/marketplace/my_listings.html b/actix_mvc_app/src/views/marketplace/my_listings.html index 6ff99bd..ecac37a 100644 --- a/actix_mvc_app/src/views/marketplace/my_listings.html +++ b/actix_mvc_app/src/views/marketplace/my_listings.html @@ -58,17 +58,17 @@ {{ listing.title }} ${{ listing.price }} - {{ listing.listing_type.as_str() }} + {{ listing.listing_type }} - {% if listing.status.as_str() == "Active" %} - {{ listing.status.as_str() }} - {% elif listing.status.as_str() == "Sold" %} - {{ listing.status.as_str() }} - {% elif listing.status.as_str() == "Cancelled" %} - {{ listing.status.as_str() }} - {% elif listing.status.as_str() == "Expired" %} - {{ listing.status.as_str() }} + {% if listing.status == "Active" %} + {{ listing.status }} + {% elif listing.status == "Sold" %} + {{ listing.status }} + {% elif listing.status == "Cancelled" %} + {{ listing.status }} + {% elif listing.status == "Expired" %} + {{ listing.status }} {% endif %} {{ listing.created_at|date }} @@ -85,7 +85,7 @@ - {% if listing.status.as_str() == "Active" %} + {% if listing.status == "Active" %}
    - -
    -
    - - DeFi Platform -
    -
    - - -
    - -
    -
    -

    Welcome to the ZAZ DeFi Platform!

    -

    Our decentralized finance platform allows you to maximize the value of your digital assets through various financial services.

    -
    -

    Use the tabs above to explore lending, borrowing, liquidity pools, staking, swapping, and collateralization features.

    -
    -
    - - -
    -
    -
    -
    -
    - Lend Your Assets -
    -
    -

    Earn interest by lending your digital assets to the ZAZ DeFi platform.

    - - -
    - - -
    - -
    - -
    - - TFT -
    -
    - -
    - - -
    - -
    -
    - Estimated Interest: - 0.00 TFT -
    -
    - Return Amount: - 0.00 TFT -
    -
    - -
    - -
    - -
    -
    -
    - -
    -
    -
    - Borrow Against Assets -
    -
    -

    Borrow digital assets using your existing assets as collateral.

    - -
    -
    - - -
    - -
    - - -
    - -
    - -
    - - TFT -
    -
    You can borrow up to 70% of your collateral value.
    -
    - -
    - - -
    - -
    -
    - Collateral Ratio: - 0% -
    -
    - Interest Due: - 0.00 TFT -
    -
    - Total Repayment: - 0.00 TFT -
    -
    -
    -
    -
    - -
    - -
    -
    -
    -
    -
    -
    - - -
    -
    - Your Active Positions -
    -
    - - -
    -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    AssetAmountInterest RateStart DateEnd DateEarned InterestStatusActions
    ThreeFold Token (TFT)1,000 TFT4.2% APY2025-04-012025-05-013.5 TFTActive - -
    Zanzibar Token (ZAZ)500 ZAZ6.8% APY2025-03-152025-06-158.5 ZAZActive - -
    -
    -
    - -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - -
    Borrowed AssetAmountCollateralCollateral RatioInterest RateStart DateDue DateActions
    ThreeFold Token (TFT)500 TFTBeach Property NFT -
    -
    -
    -
    - 65% -
    -
    5.2% APR2025-04-102025-05-10 - -
    -
    -
    -
    -
    -
    -
    - - -
    -
    -
    -
    -
    About Liquidity Pools
    -

    Liquidity pools are collections of tokens locked in smart contracts that provide liquidity for decentralized trading. By adding your assets to a liquidity pool, you earn a share of the trading fees generated by the pool.

    -
    -
    -
    - - -
    -
    -
    -
    - Available Liquidity Pools -
    -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    PoolTotal Liquidity24h VolumeAPYYour LiquidityYour ShareActions
    -
    -
    - TFT - ZAZ -
    - TFT-ZAZ -
    -
    $1,250,000$45,00012.5%$2,5000.2% -
    - - -
    -
    -
    -
    - TFT - USDT -
    - TFT-USDT -
    -
    $3,750,000$125,0008.2%$00% -
    - - -
    -
    -
    -
    - ZAZ - USDT -
    - ZAZ-USDT -
    -
    $850,000$32,00015.8%$5,0000.59% -
    - - -
    -
    -
    -
    - BTC - USDT -
    - BTC-USDT -
    -
    $5,250,000$450,0006.5%$00% -
    - - -
    -
    -
    -
    - ETH - USDT -
    - ETH-USDT -
    -
    $4,100,000$320,0007.2%$00% -
    - - -
    -
    -
    -
    -
    -
    -
    - - -
    -
    -
    -
    - Your Liquidity Positions -
    -
    -
    - -
    -
    -
    -
    -
    - TFT - ZAZ -
    - TFT-ZAZ -
    -
    -
    -
    - Your Liquidity: - $2,500 -
    -
    - Pool Share: - 0.2% -
    -
    - TFT: - 500 TFT -
    -
    - ZAZ: - 1,250 ZAZ -
    -
    - Earned Fees: - $45.20 -
    -
    - APY: - 12.5% -
    -
    - - - -
    -
    -
    -
    - - -
    -
    -
    -
    -
    - ZAZ - USDT -
    - ZAZ-USDT -
    -
    -
    -
    - Your Liquidity: - $5,000 -
    -
    - Pool Share: - 0.59% -
    -
    - ZAZ: - 2,500 ZAZ -
    -
    - USDT: - 2,500 USDT -
    -
    - Earned Fees: - $128.75 -
    -
    - APY: - 15.8% -
    -
    - - - -
    -
    -
    -
    -
    -
    -
    -
    -
    - - -
    -
    -
    -
    - Create New Liquidity Pool -
    -
    -
    -
    -
    -
    - - -
    -
    - -
    - - TFT -
    -
    -
    -
    -
    - - -
    -
    - -
    - - ZAZ -
    -
    -
    -
    -
    - -
    - 1 - TFT - = - - ZAZ -
    -
    -
    - - -
    This fee is charged on each trade and distributed to liquidity providers.
    -
    -
    - -
    -
    -
    -
    -
    -
    -
    - - -
    -
    -
    -
    -
    About Swapping
    -

    Swap allows you to exchange one token for another at the current market rate. Swaps are executed through liquidity pools with a small fee that goes to liquidity providers.

    -
    -
    -
    - -
    -
    - -
    -
    - Swap Tokens -
    -
    - -
    - - -
    - - -
    - -
    - - -
    - -
    -
    - -
    - Balance: 5,000 ZAZ - ≈ $2,500.00 -
    -
    -
    -
    - - -
    -
    -
    - Exchange Rate: - 1 TFT = 0.5 ZAZ -
    -
    - Minimum Received: - 0 ZAZ -
    -
    - Price Impact: - < 0.1% -
    -
    - Liquidity Provider Fee: - 0.3% -
    -
    -
    - - -
    - -
    -
    -
    -
    - -
    - -
    -
    - Recent Swaps -
    -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    TimeFromToValue
    2025-04-15 14:32 -
    - TFT - 500 TFT -
    -
    -
    - ZAZ - 250 ZAZ -
    -
    $250.00
    2025-04-14 09:17 -
    - USDT - 1,000 USDT -
    -
    -
    - TFT - 2,000 TFT -
    -
    $1,000.00
    2025-04-12 16:45 -
    - ZAZ - 100 ZAZ -
    -
    -
    - USDT - 50 USDT -
    -
    $50.00
    -
    -
    -
    - - -
    -
    - Market Rates -
    -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    PairRate24h ChangeVolume (24h)
    -
    -
    - TFT - ZAZ -
    - TFT/ZAZ -
    -
    0.5+2.3%$125,000
    -
    -
    - TFT - USDT -
    - TFT/USDT -
    -
    0.5-1.2%$250,000
    -
    -
    - ZAZ - USDT -
    - ZAZ/USDT -
    -
    0.5+3.7%$175,000
    -
    -
    -
    -
    -
    -
    - - -
    -
    -
    -
    -
    About Collateralization
    -

    Use your digital assets as collateral to secure loans or generate synthetic assets. Maintain a healthy collateral ratio to avoid liquidation.

    -
    -
    -
    - -
    -
    - -
    -
    - Collateralize Assets -
    -
    -
    - -
    - - -
    - - -
    - -
    - - TFT -
    -
    - Available: 10,000 TFT ($5,000) -
    -
    - - -
    - -
    - $ - -
    -
    - - -
    - - -
    - - -
    - - -
    - - -
    - -
    - $ - - -
    -
    - Maximum Loan: $0.00 -
    -
    - - - - - - - - -
    - -
    - - - - -
    -
    - - -
    - -
    - $ - - per TFT -
    -
    - Your collateral will be liquidated if the price falls below this level. -
    -
    - - -
    - -
    -
    -
    -
    -
    - -
    - -
    -
    - Your Active Collateral Positions -
    -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    AssetCollateral ValueBorrowed/GeneratedCollateral RatioLiquidation PriceStatusActions
    -
    - TFT - 2,000 TFT -
    -
    $1,000$700 (Loan) -
    -
    -
    -
    - 143% -
    -
    $0.35Healthy -
    - - -
    -
    -
    - - Beach Property NFT -
    -
    $25,00010,000 sUSD -
    -
    -
    -
    - 250% -
    -
    $10,000Warning -
    - - -
    -
    -
    - ZAZ - 1,000 ZAZ -
    -
    $5000.1 sBTC -
    -
    -
    -
    - 333% -
    -
    $0.15Healthy -
    - - -
    -
    -
    -
    -
    - - -
    -
    - Collateral Health -
    -
    -
    -
    Overall Collateral Health
    -
    -
    60%
    -
    -
    - Health score represents the overall safety of your collateral positions. Higher is better. -
    -
    - -
    -
    -
    -
    -
    Total Collateral Value
    -

    $26,500

    -
    -
    -
    -
    -
    -
    -
    Total Borrowed/Generated
    -

    $11,150

    -
    -
    -
    -
    - -
    - Your Beach Property NFT collateral is close to the liquidation threshold. Consider adding more collateral or repaying part of your synthetic assets. -
    -
    -
    -
    -
    -
    - - -
    -
    -
    -
    -
    About Staking
    -

    Staking allows you to lock your digital assets for a period of time to support network operations and earn rewards. The longer you stake, the higher rewards you can earn.

    -
    -
    -
    - - -
    -
    -
    -
    - Available Staking Options -
    -
    -
    - -
    -
    -
    -
    - TFT -
    ThreeFold Token (TFT)
    -
    -
    -
    -
    - Total Staked: - 5,250,000 TFT -
    -
    - Your Stake: - 1,000 TFT -
    -
    - APY: - 8.5% -
    - -
    - - -
    - -
    - -
    - - TFT -
    -
    - -
    -
    - Estimated Rewards: - 0 TFT -
    -
    - -
    - -
    -
    -
    -
    - - -
    -
    -
    -
    - ZAZ -
    Zanzibar Token (ZAZ)
    -
    -
    -
    -
    - Total Staked: - 2,750,000 ZAZ -
    -
    - Your Stake: - 500 ZAZ -
    -
    - APY: - 12.0% -
    - -
    - - -
    - -
    - -
    - - ZAZ -
    -
    - -
    -
    - Estimated Rewards: - 0 ZAZ -
    -
    - -
    - -
    -
    -
    -
    - - -
    -
    -
    -
    - -
    Digital Asset Staking
    -
    -
    -
    -

    Stake your NFTs and other digital assets to earn passive income.

    - -
    - - -
    - -
    - - -
    - -
    -
    - Estimated Rewards: - $0.00 -
    -
    - Reward Token: - ZAZ -
    -
    - -
    - -
    -
    -
    -
    -
    -
    -
    -
    -
    - - -
    -
    -
    -
    - Your Active Stakes -
    -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    AssetAmountValueStart DateEnd DateAPYEarned RewardsStatusActions
    -
    - TFT - ThreeFold Token (TFT) -
    -
    1,000 TFT$5002025-03-152025-06-1510.2%22.5 TFTActive - -
    -
    - ZAZ - Zanzibar Token (ZAZ) -
    -
    500 ZAZ$2502025-04-012025-05-0112.0%5.0 ZAZActive - -
    -
    - - Beach Property NFT -
    -
    1 NFT$25,0002025-02-102026-02-1010.0%450 ZAZActive - -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    diff --git a/actix_mvc_app/src/views/base.html b/actix_mvc_app/src/views/base.html index 9eeb986..57de939 100644 --- a/actix_mvc_app/src/views/base.html +++ b/actix_mvc_app/src/views/base.html @@ -150,6 +150,11 @@ Digital Assets + +
    @@ -53,83 +60,52 @@
    Contract Document
    {% if contract.status == 'Signed' %} - SIGNED + SIGNED {% elif contract.status == 'Active' %} - ACTIVE + ACTIVE {% elif contract.status == 'PendingSignatures' %} - PENDING + PENDING {% elif contract.status == 'Draft' %} - DRAFT + DRAFT {% endif %}
    - {% if contract.revisions|length > 0 %} + {% if contract_section_content_error is defined %} +
    {{ contract_section_content_error }}
    + {% endif %} + {% if contract_section_content is defined %} +
    +
    +
    + {% set section_param = section | default(value=toc[0].file) %} + {{ contract_macros::render_toc(items=toc, section_param=section_param) }} +
    +
    +
    +
    + {{ contract_section_content | safe }} +
    +
    +
    + {% elif contract.revisions|length > 0 %} {% set latest_revision = contract.latest_revision %}
    {{ latest_revision.content|safe }}
    {% else %} -
    -

    No content has been added to this contract yet.

    +
    +

    + {% if contract_section_content_error is defined %} + {{ contract_section_content_error }} + {% else %} + No content or markdown sections could be loaded for this contract. Please check the contract's content directory and Table of Contents configuration. + {% endif %} +

    {% endif %}
    - - -
    -
    -
    Signatures
    -
    -
    -
    - {% for signer in contract.signers %} -
    -
    -
    -
    {{ signer.name }}
    - - {{ signer.status }} - -
    -
    -

    {{ signer.email }}

    - - {% if signer.status == 'Signed' %} -
    - Signature -
    Signed on {{ signer.signed_at }}
    -
    - {% elif signer.status == 'Rejected' %} -
    - Rejected on {{ signer.signed_at }} -
    - {% else %} -
    -

    Waiting for signature...

    - {% if not user_has_signed %} - - {% endif %} -
    - {% endif %} - - {% if signer.comments %} -
    -

    Comments:

    -

    {{ signer.comments }}

    -
    - {% endif %} -
    -
    -
    - {% endfor %} -
    -
    -
    -
    @@ -168,7 +144,6 @@
    -
    Signers Status
    @@ -195,7 +170,6 @@
    -
    Contract Info
    @@ -223,7 +197,86 @@
    - + + +
    +
    +
    +
    +
    +
    Signatures
    +
    +
    +
    + + + + + + + + + + + + + {% for signer in contract.signers %} + + + + + + + + + {% endfor %} + +
    NameEmailStatusSigned AtCommentsActions
    {{ signer.name }}{{ signer.email }} + + {{ signer.status }} + + + {% if signer.status == 'Signed' or signer.status == 'Rejected' %} + {{ signer.signed_at }} + {% else %} + -- + {% endif %} + + {% if signer.comments %} + {{ signer.comments }} + {% else %} + -- + {% endif %} + + {% if signer.status == 'Signed' %} + + View Signed Document + + {% elif signer.status == 'Rejected' %} + + + {% else %} + {% if current_user is defined and not user_has_signed and signer.email == current_user.email %} + + {% endif %} + + {% endif %} +
    +
    +
    +
    +
    +
    +
    +
    diff --git a/actix_mvc_app/src/views/contracts/macros/contract_macros.html b/actix_mvc_app/src/views/contracts/macros/contract_macros.html new file mode 100644 index 0000000..e66fbdc --- /dev/null +++ b/actix_mvc_app/src/views/contracts/macros/contract_macros.html @@ -0,0 +1,10 @@ +{% macro render_toc(items, section_param) %} + {% for item in items %} + {{ item.title }} + {% if item.children and item.children | length > 0 %} +
    + {{ self::render_toc(items=item.children, section_param=section_param) }} +
    + {% endif %} + {% endfor %} +{% endmacro %} From 54762cb63f3cfb130253f4936e869021d6f3ce7e Mon Sep 17 00:00:00 2001 From: timurgordon Date: Mon, 5 May 2025 10:49:33 +0300 Subject: [PATCH 06/12] vocabulary change --- actix_mvc_app/README.md | 6 +++--- actix_mvc_app/src/controllers/asset.rs | 6 +++--- actix_mvc_app/src/controllers/contract.rs | 2 +- actix_mvc_app/src/controllers/flow.rs | 2 +- actix_mvc_app/src/controllers/governance.rs | 10 +++++----- actix_mvc_app/src/views/about.html | 4 ++-- actix_mvc_app/src/views/base.html | 6 +++--- actix_mvc_app/src/views/contact.html | 2 +- actix_mvc_app/src/views/contracts/contract_detail.html | 2 +- actix_mvc_app/src/views/index.html | 4 ++-- 10 files changed, 22 insertions(+), 22 deletions(-) diff --git a/actix_mvc_app/README.md b/actix_mvc_app/README.md index 8eb1ba0..8909cb1 100644 --- a/actix_mvc_app/README.md +++ b/actix_mvc_app/README.md @@ -1,4 +1,4 @@ -# Zanzibar Autonomous Zone +# Zanzibar Digital Freezone Convenience, Safety and Privacy @@ -42,8 +42,8 @@ actix_mvc_app/ 1. Clone the repository: ``` - git clone https://github.com/yourusername/zanzibar-autonomous-zone.git - cd zanzibar-autonomous-zone + git clone https://github.com/yourusername/zanzibar-digital-freezone.git + cd zanzibar-digital-freezone ``` 2. Build the project: diff --git a/actix_mvc_app/src/controllers/asset.rs b/actix_mvc_app/src/controllers/asset.rs index aae8100..fe94491 100644 --- a/actix_mvc_app/src/controllers/asset.rs +++ b/actix_mvc_app/src/controllers/asset.rs @@ -516,11 +516,11 @@ impl AssetController { let mut zaz_token = Asset { id: "asset-zaz-governance".to_string(), name: "ZAZ Governance Token".to_string(), - description: "Official governance token of the Zanzibar Autonomous Zone, used for voting on proposals and zone-wide decisions".to_string(), + description: "Official governance token of the Zanzibar Digital Freezone, used for voting on proposals and zone-wide decisions".to_string(), asset_type: AssetType::Token, status: AssetStatus::Active, owner_id: "entity-zaz-foundation".to_string(), - owner_name: "Zanzibar Autonomous Zone Foundation".to_string(), + owner_name: "Zanzibar Digital Freezone Foundation".to_string(), created_at: now - Duration::days(365), updated_at: now - Duration::days(2), blockchain_info: None, @@ -663,7 +663,7 @@ impl AssetController { "filing_date": (now - Duration::days(210)).to_rfc3339(), "grant_date": (now - Duration::days(120)).to_rfc3339(), "patent_type": "Utility", - "jurisdiction": "Zanzibar Autonomous Zone", + "jurisdiction": "Zanzibar Digital Freezone", "inventors": ["Dr. Amina Juma", "Eng. Ibrahim Hassan", "Dr. Sarah Mbeki"] }), image_url: Some("https://example.com/tidal_energy_diagram.png".to_string()), diff --git a/actix_mvc_app/src/controllers/contract.rs b/actix_mvc_app/src/controllers/contract.rs index ed860f6..476d76e 100644 --- a/actix_mvc_app/src/controllers/contract.rs +++ b/actix_mvc_app/src/controllers/contract.rs @@ -680,7 +680,7 @@ impl ContractController { toc: None, id: "contract-005".to_string(), title: "Digital Identity Verification Service Agreement".to_string(), - description: "Agreement for providing digital identity verification services to businesses operating in the Zanzibar Autonomous Zone.".to_string(), + description: "Agreement for providing digital identity verification services to businesses operating in the Zanzibar Digital Freezone.".to_string(), status: ContractStatus::Active, contract_type: ContractType::Service, created_by: "Maya Rodriguez".to_string(), diff --git a/actix_mvc_app/src/controllers/flow.rs b/actix_mvc_app/src/controllers/flow.rs index 423f50a..9853428 100644 --- a/actix_mvc_app/src/controllers/flow.rs +++ b/actix_mvc_app/src/controllers/flow.rs @@ -188,7 +188,7 @@ impl FlowController { let mut flow1 = Flow { id: "flow-1".to_string(), name: "ZAZ Business Entity Registration".to_string(), - description: "Register a new business entity within the Zanzibar Autonomous Zone legal framework".to_string(), + description: "Register a new business entity within the Zanzibar Digital Freezone legal framework".to_string(), flow_type: FlowType::CompanyRegistration, status: FlowStatus::InProgress, owner_id: "user-1".to_string(), diff --git a/actix_mvc_app/src/controllers/governance.rs b/actix_mvc_app/src/controllers/governance.rs index f1eb61c..483428e 100644 --- a/actix_mvc_app/src/controllers/governance.rs +++ b/actix_mvc_app/src/controllers/governance.rs @@ -230,7 +230,7 @@ impl GovernanceController { creator_id: 1, creator_name: "Ibrahim Faraji".to_string(), title: "Establish Zanzibar Digital Trade Hub".to_string(), - description: "This proposal aims to create a dedicated digital trade hub within the Zanzibar Autonomous Zone to facilitate international e-commerce for local businesses. The hub will provide logistics support, digital marketing services, and regulatory compliance assistance to help Zanzibar businesses reach global markets.".to_string(), + description: "This proposal aims to create a dedicated digital trade hub within the Zanzibar Digital Freezone to facilitate international e-commerce for local businesses. The hub will provide logistics support, digital marketing services, and regulatory compliance assistance to help Zanzibar businesses reach global markets.".to_string(), status: ProposalStatus::Active, created_at: now - Duration::days(5), updated_at: now - Duration::days(5), @@ -242,7 +242,7 @@ impl GovernanceController { creator_id: 2, creator_name: "Amina Salim".to_string(), title: "ZAZ Sustainable Tourism Framework".to_string(), - description: "A comprehensive framework for sustainable tourism development within the Zanzibar Autonomous Zone. This proposal outlines environmental standards, community benefit-sharing mechanisms, and digital infrastructure for eco-tourism businesses. It includes tokenization standards for tourism assets and a certification system for sustainable operators.".to_string(), + description: "A comprehensive framework for sustainable tourism development within the Zanzibar Digital Freezone. This proposal outlines environmental standards, community benefit-sharing mechanisms, and digital infrastructure for eco-tourism businesses. It includes tokenization standards for tourism assets and a certification system for sustainable operators.".to_string(), status: ProposalStatus::Approved, created_at: now - Duration::days(15), updated_at: now - Duration::days(2), @@ -266,7 +266,7 @@ impl GovernanceController { creator_id: 1, creator_name: "Ibrahim Faraji".to_string(), title: "ZAZ Regulatory Framework for Digital Financial Services".to_string(), - description: "Establish a comprehensive regulatory framework for digital financial services within the Zanzibar Autonomous Zone. This includes licensing requirements for crypto exchanges, digital payment providers, and tokenized asset platforms operating within the zone, while ensuring compliance with international AML/KYC standards.".to_string(), + description: "Establish a comprehensive regulatory framework for digital financial services within the Zanzibar Digital Freezone. This includes licensing requirements for crypto exchanges, digital payment providers, and tokenized asset platforms operating within the zone, while ensuring compliance with international AML/KYC standards.".to_string(), status: ProposalStatus::Rejected, created_at: now - Duration::days(20), updated_at: now - Duration::days(5), @@ -290,7 +290,7 @@ impl GovernanceController { creator_id: 5, creator_name: "Omar Makame".to_string(), title: "Zanzibar Renewable Energy Microgrid Network".to_string(), - description: "Develop a network of renewable energy microgrids across the Zanzibar Autonomous Zone using tokenized investment and community ownership models. This proposal outlines the technical specifications, governance structure, and token economics for deploying solar and tidal energy systems that will ensure energy independence for the zone.".to_string(), + description: "Develop a network of renewable energy microgrids across the Zanzibar Digital Freezone using tokenized investment and community ownership models. This proposal outlines the technical specifications, governance structure, and token economics for deploying solar and tidal energy systems that will ensure energy independence for the zone.".to_string(), status: ProposalStatus::Active, created_at: now - Duration::days(10), updated_at: now - Duration::days(9), @@ -302,7 +302,7 @@ impl GovernanceController { creator_id: 6, creator_name: "Saida Juma".to_string(), title: "ZAZ Educational Technology Initiative".to_string(), - description: "Establish a comprehensive educational technology program within the Zanzibar Autonomous Zone to develop local tech talent. This initiative includes coding academies, blockchain development courses, and digital entrepreneurship training, with a focus on preparing Zanzibar's youth for careers in the zone's growing digital economy.".to_string(), + description: "Establish a comprehensive educational technology program within the Zanzibar Digital Freezone to develop local tech talent. This initiative includes coding academies, blockchain development courses, and digital entrepreneurship training, with a focus on preparing Zanzibar's youth for careers in the zone's growing digital economy.".to_string(), status: ProposalStatus::Draft, created_at: now - Duration::days(3), updated_at: now - Duration::days(2), diff --git a/actix_mvc_app/src/views/about.html b/actix_mvc_app/src/views/about.html index d7e7774..3bc6dda 100644 --- a/actix_mvc_app/src/views/about.html +++ b/actix_mvc_app/src/views/about.html @@ -1,13 +1,13 @@ {% extends "base.html" %} -{% block title %}About - Zanzibar Autonomous Zone{% endblock %} +{% block title %}About - Zanzibar Digital Freezone{% endblock %} {% block content %}
    -

    About Zanzibar Autonomous Zone

    +

    About Zanzibar Digital Freezone

    Convenience, Safety and Privacy

    Technology Stack

    diff --git a/actix_mvc_app/src/views/base.html b/actix_mvc_app/src/views/base.html index 57de939..793fd54 100644 --- a/actix_mvc_app/src/views/base.html +++ b/actix_mvc_app/src/views/base.html @@ -3,7 +3,7 @@ - {% block title %}Zanzibar Autonomous Zone{% endblock %} + {% block title %}Zanzibar Digital Freezone{% endblock %} @@ -64,7 +64,7 @@ -
    Zanzibar Autonomous Zone
    +
    Zanzibar Digital Freezone
    - © 2024 Zanzibar Autonomous Zone + © 2024 Zanzibar Digital Freezone
    diff --git a/actix_mvc_app/src/views/contact.html b/actix_mvc_app/src/views/contact.html index d77137d..9436628 100644 --- a/actix_mvc_app/src/views/contact.html +++ b/actix_mvc_app/src/views/contact.html @@ -1,6 +1,6 @@ {% extends "base.html" %} -{% block title %}Contact - Zanzibar Autonomous Zone{% endblock %} +{% block title %}Contact - Zanzibar Digital Freezone{% endblock %} {% block content %}
    diff --git a/actix_mvc_app/src/views/contracts/contract_detail.html b/actix_mvc_app/src/views/contracts/contract_detail.html index 376124e..707c947 100644 --- a/actix_mvc_app/src/views/contracts/contract_detail.html +++ b/actix_mvc_app/src/views/contracts/contract_detail.html @@ -342,7 +342,7 @@ {% if contract.organization %}

    {{ contract.organization }}

    - Registered in Zanzibar Autonomous Zone + Registered in Zanzibar Digital Freezone

    {% else %}

    No organization specified

    diff --git a/actix_mvc_app/src/views/index.html b/actix_mvc_app/src/views/index.html index ebcb76e..559ecd8 100644 --- a/actix_mvc_app/src/views/index.html +++ b/actix_mvc_app/src/views/index.html @@ -1,14 +1,14 @@ {% extends "base.html" %} {# Updated template with card blocks - 2025-04-22 #} -{% block title %}Home - Zanzibar Autonomous Zone{% endblock %} +{% block title %}Home - Zanzibar Digital Freezone{% endblock %} {% block content %}
    -

    Zanzibar Autonomous Zone

    +

    Zanzibar Digital Freezone

    Convenience, Safety and Privacy

    +{% endblock %} + +{% block content %} + +{% if success_message %} +
    + +
    +{% endif %} + +
    +

    Company & Legal Entity Management (Freezone)

    + + +
    +
    + + +
    +
    + {% include "company/manage.html" %} +
    +
    + {% include "company/register.html" %} +
    +
    +
    +
    +
    +{% endblock %} + +{% block scripts %} + {{ super() }} + + +{% endblock %} diff --git a/actix_mvc_app/src/views/company/manage.html b/actix_mvc_app/src/views/company/manage.html new file mode 100644 index 0000000..fdfaa88 --- /dev/null +++ b/actix_mvc_app/src/views/company/manage.html @@ -0,0 +1,193 @@ +
    +
    + Your Companies +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeStatusDate RegisteredActions
    Zanzibar Digital SolutionsStartup FZCActive2025-04-01 + +
    Blockchain Innovations LtdGrowth FZCActive2025-03-15 + +
    Sustainable Energy CooperativeCooperative FZCPending2025-05-01 + +
    +
    +
    + + + diff --git a/actix_mvc_app/src/views/company/register.html b/actix_mvc_app/src/views/company/register.html new file mode 100644 index 0000000..63b16d3 --- /dev/null +++ b/actix_mvc_app/src/views/company/register.html @@ -0,0 +1,1196 @@ +
    +
    + Register a New Company / Legal Entity +
    +
    +
    + +
    +
    Step 1 of 4
    +
    + + +
    +
    + 1 General Info +
    +
    + 2 Company Type +
    +
    + 3 Shareholders +
    +
    + 4 Documents +
    +
    + + +
    +

    General Company Information

    +
    + + +
    Choose a unique name for your company or entity.
    +
    +
    + + +
    +
    + + +
    +
    + +
    +
    + + + + + + + + + +
    +
    +
    + + + diff --git a/actix_mvc_app/src/views/company/tabs.html b/actix_mvc_app/src/views/company/tabs.html new file mode 100644 index 0000000..61aff51 --- /dev/null +++ b/actix_mvc_app/src/views/company/tabs.html @@ -0,0 +1,21 @@ + + +
    +
    + {% include "company/manage.html" %} +
    +
    + {% include "company/register.html" %} +
    +
    diff --git a/actix_mvc_app/src/views/company/view.html b/actix_mvc_app/src/views/company/view.html new file mode 100644 index 0000000..b3a8d0c --- /dev/null +++ b/actix_mvc_app/src/views/company/view.html @@ -0,0 +1,177 @@ +{% extends "base.html" %} + +{% block title %}{{ company_name }} - Company Details{% endblock %} + +{% block head %} + {{ super() }} + +{% endblock %} + +{% block content %} +
    +
    +

    {{ company_name }}

    + +
    + +
    +
    +
    +
    +
    General Information
    +
    +
    + + + + + + + + + + + + + + + + + + + + + +
    Company Name:{{ company_name }}
    Type:{{ company_type }}
    Registration Date:{{ registration_date }}
    Status: + {% if status == "Active" %} + {{ status }} + {% else %} + {{ status }} + {% endif %} +
    Purpose:{{ purpose }}
    +
    +
    +
    +
    +
    +
    +
    Billing Information
    +
    +
    + + + + + + + + + + + + + +
    Plan:{{ plan }}
    Next Billing:{{ next_billing }}
    Payment Method:{{ payment_method }}
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    Shareholders
    +
    +
    + + + + + + + + + {% for shareholder in shareholders %} + + + + + {% endfor %} + +
    NamePercentage
    {{ shareholder.0 }}{{ shareholder.1 }}
    +
    +
    +
    +
    +
    +
    +
    Contracts
    +
    +
    + + + + + + + + + + {% for contract in contracts %} + + + + + + {% endfor %} + +
    ContractStatusAction
    {{ contract.0 }} + {% if contract.1 == "Signed" %} + {{ contract.1 }} + {% else %} + {{ contract.1 }} + {% endif %} + + View +
    +
    +
    +
    +
    + +
    +
    +
    Actions
    +
    + +
    +
    +{% endblock %} + +{% block scripts %} + {{ super() }} + +{% endblock %} diff --git a/actix_mvc_app/src/views/defi/tabs/collateral.html b/actix_mvc_app/src/views/defi/tabs/collateral.html index 401e75c..7d67859 100644 --- a/actix_mvc_app/src/views/defi/tabs/collateral.html +++ b/actix_mvc_app/src/views/defi/tabs/collateral.html @@ -24,11 +24,11 @@ - - @@ -180,7 +180,7 @@
    - TFT + 2,000 TFT
    @@ -232,7 +232,7 @@
    - ZDFZ + 1,000 ZDFZ
    diff --git a/actix_mvc_app/src/views/defi/tabs/lending_borrowing.html b/actix_mvc_app/src/views/defi/tabs/lending_borrowing.html index fc21d41..480f88b 100644 --- a/actix_mvc_app/src/views/defi/tabs/lending_borrowing.html +++ b/actix_mvc_app/src/views/defi/tabs/lending_borrowing.html @@ -179,7 +179,7 @@
    -
    + {{ position.base.asset_name }}
    @@ -232,14 +232,14 @@
    -
    + {{ position.base.asset_name }}
    {{ position.base.amount }} {{ position.base.asset_symbol }}
    -
    + {{ position.collateral_amount }} {{ position.collateral_asset_symbol }}
    diff --git a/actix_mvc_app/src/views/defi/tabs/liquidity.html b/actix_mvc_app/src/views/defi/tabs/liquidity.html index 3070cef..0895821 100644 --- a/actix_mvc_app/src/views/defi/tabs/liquidity.html +++ b/actix_mvc_app/src/views/defi/tabs/liquidity.html @@ -34,8 +34,8 @@
    - TFT - ZDFZ + +
    TFT-ZDFZ
    @@ -56,8 +56,8 @@
    - TFT - USDT + +
    TFT-USDT
    @@ -76,13 +76,7 @@ -
    -
    - ZDFZ - USDT -
    - ZDFZ-USDT -
    + ZDFZ-USDT $850,000 $32,000 @@ -117,13 +111,7 @@
    -
    -
    - TFT - ZDFZ -
    - TFT-ZDFZ -
    + TFT-ZDFZ
    @@ -163,13 +151,7 @@
    -
    -
    - ZDFZ - USDT -
    - ZDFZ-USDT -
    + ZDFZ-USDT
    diff --git a/actix_mvc_app/src/views/defi/tabs/providing_receiving.html b/actix_mvc_app/src/views/defi/tabs/providing_receiving.html index 2d8a2a3..9e99400 100644 --- a/actix_mvc_app/src/views/defi/tabs/providing_receiving.html +++ b/actix_mvc_app/src/views/defi/tabs/providing_receiving.html @@ -156,7 +156,7 @@ This is a compliant version of the previous lending_borrowing.html tab. All term
    -
    + {{ position.base.asset_name }}
    @@ -211,16 +211,13 @@ This is a compliant version of the previous lending_borrowing.html tab. All term
    -
    + {{ position.base.asset_name }}
    {{ position.base.amount }} {{ position.base.asset_symbol }} -
    -
    - {{ position.collateral_amount }} {{ position.collateral_asset_symbol }} -
    + {{ position.collateral_amount }} {{ position.collateral_asset_symbol }}
    diff --git a/actix_mvc_app/src/views/defi/tabs/staking.html b/actix_mvc_app/src/views/defi/tabs/staking.html index d11eb21..7552172 100644 --- a/actix_mvc_app/src/views/defi/tabs/staking.html +++ b/actix_mvc_app/src/views/defi/tabs/staking.html @@ -22,7 +22,6 @@
    - TFT
    ThreeFold Token (TFT)
    @@ -80,7 +79,6 @@
    - ZDFZ
    Zanzibar Token (ZDFZ)
    @@ -221,7 +219,7 @@
    - TFT + ThreeFold Token (TFT)
    @@ -239,7 +237,7 @@
    - ZDFZ + Zanzibar Token (ZDFZ)
    diff --git a/actix_mvc_app/src/views/defi/tabs/swap.html b/actix_mvc_app/src/views/defi/tabs/swap.html index 54b6edf..8336c03 100644 --- a/actix_mvc_app/src/views/defi/tabs/swap.html +++ b/actix_mvc_app/src/views/defi/tabs/swap.html @@ -29,27 +29,26 @@