Spaces:
Build error
Build error
| import streamlit as st | |
| from utils.transfer_utils import transfer_apt_sync | |
| from components.auth_component import one_round_auth | |
| from pages import app | |
| # Registration Page | |
| st.header("๐ Registration") | |
| if not app.wallet: | |
| st.error("โ Please connect a wallet first") | |
| st.info("๐ Go to 'Import/Generate Wallet' to get started") | |
| st.stop() | |
| if app.is_registered: | |
| st.success("โ You are already registered!") | |
| st.info("๐ Go to 'Authentication' to verify your 1P secret") | |
| st.stop() | |
| st.markdown(""" | |
| ### Registration Process: | |
| 1. **Select your 1P secret** - Choose one UTF-8 character elegantly | |
| 2. **Configure direction mapping** - Set your color-to-direction preferences | |
| 3. **Authenticate yourself** - Verify your 1P secret with a quick challenge | |
| 4. **Transfer minimum 1 APT** - Funds will be held in our secure system wallet | |
| 5. **Complete registration** - Your wallet will be registered for 1P authentication | |
| """) | |
| # Step 1: UTF-8 Character Selection | |
| st.markdown("---") | |
| st.subheader("๐จ Step 1: Select Your 1P Secret") | |
| st.markdown("Choose **one character** that will be your secret. No keyboard typing required!") | |
| # Language/Category filters | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| category_type = st.selectbox( | |
| "Category Type", | |
| options=["Emojis & Symbols", "Languages", "All Categories"], | |
| index=0, | |
| help="Filter by type of character categories" | |
| ) | |
| # Dynamically set options based on category type | |
| if category_type == "Emojis & Symbols": | |
| category_options = ['emojis', 'hearts', 'nature', 'food', 'animals', 'travel', 'sports', 'tech', 'music', 'weather', 'zodiac', 'numbers', 'symbols', 'ascii'] | |
| elif category_type == "Languages": | |
| category_options = ['japanese', 'korean', 'chinese', 'arabic', 'cyrillic', 'ascii'] | |
| else: | |
| category_options = list(DOMAINS.keys()) | |
| selected_categories = st.multiselect( | |
| "Character Categories", | |
| options=category_options, | |
| default=['emojis'] if category_type == "Emojis & Symbols" else ['ascii'] if category_type == "Languages" else ['emojis'], | |
| help="Select which types of characters to show" | |
| ) | |
| with col2: | |
| col2_1, col2_2 = st.columns(2) | |
| with col2_1: | |
| chars_per_row = st.slider("Characters per row", 5, 20, 10) | |
| with col2_2: | |
| show_unicode = st.checkbox("Show Unicode codes", False) | |
| search_term = st.text_input("Search (emoji description or character)", "", | |
| placeholder="heart, food, smile, etc.", | |
| help="Filter characters by description") | |
| # Build character set based on selection | |
| available_chars = "" | |
| for category in selected_categories: | |
| available_chars += DOMAINS[category] | |
| if not available_chars: | |
| st.warning("Please select at least one character category") | |
| st.stop() | |
| # Apply search filter if provided | |
| chars_list = list(set(available_chars)) # Remove duplicates | |
| if search_term: | |
| # Simple filtering mechanism | |
| search_term = search_term.lower() | |
| # Define some common emoji descriptions for better search | |
| emoji_descriptions = { | |
| 'smile': '๐๐๐๐๐', | |
| 'laugh': '๐๐คฃ', | |
| 'heart': 'โค๏ธ๐๐๐๐๐๐๐๐๐งก๐๐๐', | |
| 'food': '๐๐๐๐๐๐๐๐๐ฅญ๐๐๐ฅ๐๐ฅฅ๐ ๐ฅ๐๐ฅ๐ฅ๐ฝ', | |
| 'animal': '๐ถ๐ฑ๐ญ๐น๐ฐ๐ฆ๐ป๐ผ๐จ๐ฆ๐ฏ๐ฎ๐ท๐ธ๐ต๐', | |
| 'flower': '๐ธ๐บ๐ป๐ท๐น๐ผ', | |
| 'star': 'โญ๐๐ซโจ', | |
| 'face': '๐๐๐๐๐๐ ๐๐คฃ๐๐๐๐๐๐๐', | |
| 'hand': '๐๐๐โ๏ธ๐ค๐ค๐ค๐โ๐ค๐ค๐', | |
| 'music': '๐ต๐ถ๐ธ๐น๐ท๐บ๐ป๐ฅ๐ผ', | |
| 'sport': 'โฝโพ๐๐๐๐๐พ๐๐ธ', | |
| 'travel': 'โ๏ธ๐๐๐๐๐ฒ', | |
| 'weather': 'โ๏ธ๐ค๏ธโ ๐ฅ๏ธโ๏ธ๐ฆ๏ธ๐ง๏ธโ๏ธ' | |
| } | |
| filtered_chars = [] | |
| for char in chars_list: | |
| # Check if char is in any of the emoji description groups that match the search term | |
| in_description = False | |
| for desc, emoji_group in emoji_descriptions.items(): | |
| if desc.lower().find(search_term) >= 0 and char in emoji_group: | |
| in_description = True | |
| break | |
| # Add char if it matches search | |
| if in_description or char.lower() == search_term.lower(): | |
| filtered_chars.append(char) | |
| chars_list = filtered_chars if filtered_chars else chars_list | |
| # Sort the characters | |
| chars_list.sort() | |
| # Create a pagination system for large character sets | |
| chars_per_page = chars_per_row * 5 # 5 rows per page | |
| total_chars = len(chars_list) | |
| total_pages = (total_chars + chars_per_page - 1) // chars_per_page # Ceiling division | |
| # Only show pagination if needed | |
| if total_pages > 1: | |
| col1, col2, col3 = st.columns([1, 3, 1]) | |
| with col2: | |
| page_num = st.select_slider("Page", options=list(range(1, total_pages + 1)), value=1) | |
| else: | |
| page_num = 1 | |
| start_idx = (page_num - 1) * chars_per_page | |
| end_idx = min(start_idx + chars_per_page, total_chars) | |
| # Display character selection grid | |
| st.markdown(f"**Available Characters:** ({total_chars} characters found)") | |
| visible_chars = chars_list[start_idx:end_idx] | |
| # Create grid display | |
| rows = [visible_chars[i:i + chars_per_row] for i in range(0, len(visible_chars), chars_per_row)] | |
| selected_secret = None | |
| for row_idx, row in enumerate(rows): | |
| cols = st.columns(len(row)) | |
| for col_idx, char in enumerate(row): | |
| with cols[col_idx]: | |
| unicode_info = f"\\nU+{ord(char):04X}" if show_unicode else "" | |
| if st.button(f"{char}{unicode_info}", | |
| key=f"char_{row_idx}_{col_idx}_p{page_num}", | |
| use_container_width=True): | |
| selected_secret = char | |
| app.selected_secret = char | |
| app.save_to_session() | |
| st.rerun() | |
| # Show recently used characters for quick selection | |
| if not app.selected_secret and (app.recent_characters or app.favorite_characters): | |
| st.markdown("---") | |
| st.subheader("โญ Quick Selection") | |
| # Show favorites if available | |
| if app.favorite_characters: | |
| st.markdown("**Favorite Characters:**") | |
| fav_cols = st.columns(min(10, len(app.favorite_characters))) | |
| for idx, char in enumerate(app.favorite_characters): | |
| with fav_cols[idx % len(fav_cols)]: | |
| if st.button(f"{char}", | |
| key=f"fav_{idx}", | |
| use_container_width=True): | |
| app.selected_secret = char | |
| app.save_to_session() | |
| st.rerun() | |
| # Show recent characters if available | |
| if app.recent_characters: | |
| st.markdown("**Recently Used:**") | |
| recent_cols = st.columns(min(10, len(app.recent_characters))) | |
| for idx, char in enumerate(app.recent_characters): | |
| with recent_cols[idx % len(recent_cols)]: | |
| if st.button(f"{char}", | |
| key=f"recent_{idx}", | |
| use_container_width=True): | |
| app.selected_secret = char | |
| # Add to favorites | |
| with recent_cols[idx % len(recent_cols)]: | |
| if st.button("โญ", key=f"fav_add_{idx}", help="Add to favorites"): | |
| if char not in app.favorite_characters: | |
| app.favorite_characters.append(char) | |
| if len(app.favorite_characters) > 10: | |
| app.favorite_characters.pop(0) # Remove oldest if over limit | |
| app.save_to_session() | |
| st.rerun() | |
| # Show selected secret | |
| if app.selected_secret: | |
| st.success(f"โ Selected secret: **{app.selected_secret}** (U+{ord(app.selected_secret):04X})") | |
| # Add selected character to recent list if not already there | |
| if app.selected_secret not in app.recent_characters: | |
| app.recent_characters.append(app.selected_secret) | |
| # Keep only the last 10 characters | |
| if len(app.recent_characters) > 10: | |
| app.recent_characters.pop(0) | |
| app.save_to_session() | |
| # Option to add to favorites | |
| col1, col2 = st.columns([3, 1]) | |
| with col2: | |
| if app.selected_secret not in app.favorite_characters: | |
| if st.button("โญ Add to Favorites"): | |
| app.favorite_characters.append(app.selected_secret) | |
| if len(app.favorite_characters) > 10: | |
| app.favorite_characters.pop(0) # Remove oldest if over limit | |
| app.save_to_session() | |
| st.rerun() | |
| else: | |
| if st.button("โ Remove from Favorites"): | |
| app.favorite_characters.remove(app.selected_secret) | |
| app.save_to_session() | |
| st.rerun() | |
| # Step 2: Direction Mapping Configuration | |
| if app.selected_secret: | |
| st.markdown("---") | |
| st.subheader("๐งญ Step 2: Configure Direction Mapping") | |
| st.markdown("Set your preferred directions for each color. This will be used during authentication.") | |
| col1, col2 = st.columns(2) | |
| direction_mapping = {} | |
| with col1: | |
| st.markdown("**Color โ Direction Mapping:**") | |
| for color in COLORS: | |
| direction_mapping[color] = st.selectbox( | |
| f"{color.title()} โ", | |
| options=DIRECTIONS, | |
| key=f"direction_{color}", | |
| index=DIRECTIONS.index(DIRECTIONS[COLORS.index(color)]) # Default mapping | |
| ) | |
| with col2: | |
| st.markdown("**Preview:**") | |
| for color, direction in direction_mapping.items(): | |
| emoji_map = {"Up": "โฌ๏ธ", "Down": "โฌ๏ธ", "Left": "โฌ ๏ธ", "Right": "โก๏ธ", "Skip": "โญ๏ธ"} | |
| st.text(f"{color.title()}: {direction} {emoji_map[direction]}") | |
| app.direction_mapping = direction_mapping | |
| app.save_to_session() | |
| # Step 3: Authentication | |
| if app.selected_secret and app.direction_mapping: | |
| st.markdown("---") | |
| st.subheader("๐ Step 3: Authenticate Yourself") | |
| st.markdown("""Verify your identity using the 1P visual grid system. This ensures you remember your secret | |
| character and color-to-direction mapping before proceeding with fund transfer.""") | |
| # Show authentication challenge using the component | |
| auth_success = one_round_auth( | |
| secret=app.selected_secret, | |
| direction_mapping=app.direction_mapping, | |
| colors=COLORS, | |
| direction_map=DIRECTION_MAP, | |
| domains=DOMAINS, | |
| session_key="registration_auth" | |
| ) | |
| if not auth_success: | |
| # If auth is not completed or failed, stop here | |
| if app.registration_auth and app.registration_auth.get("completed"): | |
| if not app.registration_auth.get("success"): | |
| st.error("โ Authentication failed! Please try again.") | |
| if st.button("๐ Try Again", key="auth_retry"): | |
| # Reset auth state | |
| app.registration_auth = { | |
| 'started': False, | |
| 'completed': False, | |
| 'success': False, | |
| 'grid_html': None, | |
| 'expected': None | |
| } | |
| app.save_to_session() | |
| st.rerun() | |
| st.stop() | |
| st.success("โ Authentication successful!") | |
| # Step 4: Balance Transfer | |
| if app.selected_secret and app.direction_mapping and app.registration_auth and app.registration_auth.get("completed") and app.registration_auth.get("success"): | |
| st.markdown("---") | |
| st.subheader("๐ฐ Step 4: Transfer Funds for Registration") | |
| st.markdown("**Why transfer funds?**") | |
| st.markdown(""" | |
| - Transfers 1 APT minimum to register for the 1P system | |
| - Your funds are held securely in our system wallet | |
| - Transactions are processed through our secure backend | |
| - Your private key is never exposed after registration | |
| """) | |
| # Check current balance automatically | |
| with st.spinner("Checking wallet balance..."): | |
| try: | |
| # Try to use the sync helper method | |
| apt_balance = app.get_account_balance_sync(app.wallet.address()) | |
| # Display balance with colorful metric | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| st.metric("Current Wallet Balance", f"{apt_balance} APT") | |
| with col2: | |
| if apt_balance >= 1.0: | |
| st.success("โ Sufficient balance for registration") | |
| else: | |
| st.error("โ Insufficient balance. Need at least 1 APT.") | |
| st.warning("Please use the faucet in the wallet setup page.") | |
| st.stop() | |
| # Add refresh button | |
| if st.button("๐ Refresh Balance", type="secondary"): | |
| st.rerun() | |
| except Exception as e: | |
| st.error(f"Balance check error: {str(e)}") | |
| st.warning("Unable to check balance automatically. You can proceed if you know you have sufficient funds (at least 1 APT).") | |
| # Add manual balance check option | |
| if st.button("Try Again", type="secondary"): | |
| st.rerun() | |
| # Provide option to continue anyway | |
| st.info("If you're certain you have at least 1 APT, you can continue with the registration.") | |
| # Option to proceed anyway | |
| if not st.checkbox("I understand the risks and want to proceed anyway"): | |
| st.stop() | |
| # Transfer amount selection | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| transfer_amount = st.number_input( | |
| "Transfer Amount (APT)", | |
| min_value=1.0, | |
| max_value=100.0, | |
| value=1.0, | |
| step=0.1, | |
| help="Minimum 1 APT required" | |
| ) | |
| with col2: | |
| st.markdown("<br>", unsafe_allow_html=True) # Spacing | |
| leave_for_gas = st.checkbox("Leave 0.1 APT for gas fees", value=True) | |
| # Step 5: Complete Registration | |
| st.markdown("---") | |
| st.subheader("โ Step 5: Complete Registration") | |
| st.warning("โ ๏ธ **Final Check:**") | |
| st.markdown(f""" | |
| - **Secret Character:** {app.selected_secret} | |
| - **Direction Mapping:** {len(app.direction_mapping)} colors configured | |
| - **Transfer Amount:** {transfer_amount} APT | |
| - **From Wallet:** `{app.wallet.address()[:10]}...` | |
| """) | |
| st.error("๐ **Important:** After registration, your wallet's private key will be securely handled by our system. Make sure you're ready to proceed.") | |
| confirm_registration = st.checkbox("I understand and want to proceed with registration") | |
| if confirm_registration and st.button("๐ Complete Registration", type="primary"): | |
| with st.spinner("Processing registration..."): | |
| try: | |
| # Check user wallet balance first | |
| apt_balance = app.get_account_balance_sync(app.wallet.address()) | |
| if apt_balance < transfer_amount: | |
| st.error(f"โ Insufficient balance: You have {apt_balance} APT but are trying to transfer {transfer_amount} APT") | |
| st.warning("Please get more APT from the faucet or reduce the transfer amount.") | |
| st.stop() | |
| # Use our abstracted transfer function | |
| with st.spinner("Creating and processing transaction..."): | |
| success, txn_hash, error_msg = transfer_apt_sync( | |
| sender_account=app.wallet, | |
| recipient_address=SYSTEM_WALLET_ADDRESS, | |
| amount_apt=transfer_amount | |
| ) | |
| if not success: | |
| st.error(f"Transaction failed: {error_msg}") | |
| st.warning("Please check your balance and try again.") | |
| st.stop() | |
| # Mark as registered and record the transaction | |
| app.is_registered = True | |
| # Record transaction in our history | |
| app.add_transaction( | |
| txn_hash=txn_hash, | |
| sender=str(app.wallet.address()), | |
| recipient=SYSTEM_WALLET_ADDRESS, | |
| amount=transfer_amount, | |
| is_credit=False, | |
| status="completed", | |
| description="1P Wallet Registration" | |
| ) | |
| # Persist changes to session | |
| app.save_to_session() | |
| st.success("๐ Registration completed successfully!") | |
| st.success(f"โ Transaction Hash: `{txn_hash}`") | |
| st.info("**Next:** Go to 'Authentication' to verify your 1P secret") | |
| st.markdown("๐ You can view this transaction in your **Transaction History** page") | |
| # Show registration summary | |
| with st.expander("Registration Summary", expanded=True): | |
| st.markdown(f""" | |
| - **Wallet:** `{app.wallet.address()}` | |
| - **Secret:** {app.selected_secret} (U+{ord(app.selected_secret):04X}) | |
| - **Amount Transferred:** {transfer_amount} APT | |
| - **Transaction:** `{txn_hash}` | |
| - **System Wallet:** `{SYSTEM_WALLET_ADDRESS}` | |
| """) | |
| except Exception as e: | |
| st.error(f"โ Registration failed: {str(e)}") | |
| st.error("Please check your balance and try again") |