import streamlit as st import os import secrets import hashlib import tempfile import subprocess import json from starknet_py.net.account.account import Account from starknet_py.net.models import StarknetChainId from starknet_py.net.signer.stark_curve_signer import KeyPair from starknet_py.net.gateway_client import GatewayClient from starknet_py.contract import Contract # Set page config st.set_page_config( page_title="StarkNet 開發工作流程", page_icon="🌟", layout="wide" ) # Application title st.title("StarkNet 開發完整工作流程") st.markdown(""" 這個應用演示了進行 StarkNet 開發的完整流程,包括: 1. 編寫和編譯 Cairo 合約 2. 生成 StarkNet 密鑰對和地址 3. 與 StarkNet 進行交互 """) # Sidebar st.sidebar.header("StarkNet 開發工具") selected_section = st.sidebar.radio( "選擇步驟", ["編寫和編譯合約", "生成密鑰對和地址", "與 StarkNet 交互"] ) # Function to compile Cairo contract def compile_cairo_contract(cairo_code): with tempfile.NamedTemporaryFile(suffix='.cairo', delete=False) as tmp_file: tmp_file.write(cairo_code.encode()) tmp_file_path = tmp_file.name output_file = f"{tmp_file_path}_compiled.json" try: result = subprocess.run( ["cairo-compile", tmp_file_path, "--output", output_file], capture_output=True, text=True, check=True ) # Read compiled contract with open(output_file, 'r') as f: compiled_contract = json.load(f) # Clean up temporary files os.remove(tmp_file_path) os.remove(output_file) return compiled_contract, result.stdout except subprocess.CalledProcessError as e: return None, e.stderr except Exception as e: return None, str(e) # Function to generate StarkNet keys def generate_starknet_keys(): # Generate random private key (251 bits for StarkNet) private_key = secrets.randbits(251) private_key_hex = hex(private_key) # Create key pair key_pair = KeyPair.from_private_key(private_key) # Get public key public_key = key_pair.public_key public_key_hex = hex(public_key) # Try to get an appropriate chain ID try: chain_id = StarknetChainId.GOERLI except AttributeError: try: chain_id = StarknetChainId.SEPOLIA except AttributeError: # Use a generic testnet ID if above networks are not available chain_id = 1536727068981429685321 # Create account object account = Account( address=0, # Will be populated after deployment client=None, # Not needed for this example key_pair=key_pair, chain=chain_id ) # Get account address address = account.address return { "private_key": private_key_hex, "public_key": public_key_hex, "address": hex(address), "chain_id": chain_id, "key_pair": key_pair } # Section 1: Write and Compile Cairo Contract if selected_section == "編寫和編譯合約": st.header("1. 編寫和編譯 Cairo 合約") # Default Cairo contract template default_cairo_code = """%lang cairo @external func transfer{syscall_ptr: felt*, range_check_ptr}( from_address: felt, to_address: felt, amount: felt ) -> (success: felt): # 這只是一個簡單的模擬轉帳函數 # 在實際應用中,您需要進行餘額檢查和狀態更新 return (1) # 返回成功 end @view func get_balance{syscall_ptr: felt*, range_check_ptr}( address: felt ) -> (balance: felt): # 這只是一個簡單的模擬餘額查詢函數 # 在實際應用中,您需要從存儲中讀取真實的餘額 return (1000) # 返回模擬餘額 end """ cairo_code = st.text_area("Cairo 合約代碼", default_cairo_code, height=400) if st.button("編譯合約"): with st.spinner("正在編譯 Cairo 合約..."): compiled_contract, output = compile_cairo_contract(cairo_code) if compiled_contract: st.success("合約編譯成功!") st.code(json.dumps(compiled_contract, indent=2), language="json") # Save the compiled contract to session state for later use st.session_state['compiled_contract'] = compiled_contract else: st.error("合約編譯失敗!") st.code(output) # Section 2: Generate StarkNet Keys elif selected_section == "生成密鑰對和地址": st.header("2. 生成 StarkNet 密鑰對和地址") if st.button("生成新的密鑰對"): with st.spinner("正在生成 StarkNet 密鑰..."): keys_info = generate_starknet_keys() # Save keys to session state st.session_state['keys_info'] = keys_info # Display keys information st.subheader("StarkNet 密鑰信息") st.markdown(f""" - **私鑰**: `{keys_info['private_key']}` - **公鑰**: `{keys_info['public_key']}` - **地址**: `{keys_info['address']}` - **網絡**: `{keys_info['chain_id']}` """) # Option to download keys as text file keys_text = f"""私鑰: {keys_info['private_key']} 公鑰: {keys_info['public_key']} 地址: {keys_info['address']} 網絡: {keys_info['chain_id']}""" st.download_button( label="下載密鑰信息", data=keys_text, file_name="starknet_keys.txt", mime="text/plain" ) # Display saved keys if available if 'keys_info' in st.session_state: st.subheader("已保存的 StarkNet 密鑰信息") st.markdown(f""" - **私鑰**: `{st.session_state['keys_info']['private_key']}` - **公鑰**: `{st.session_state['keys_info']['public_key']}` - **地址**: `{st.session_state['keys_info']['address']}` - **網絡**: `{st.session_state['keys_info']['chain_id']}` """) # Section 3: Interact with StarkNet elif selected_section == "與 StarkNet 交互": st.header("3. 與 StarkNet 交互") # Network selection network = st.selectbox( "選擇 StarkNet 網絡", ["goerli", "sepolia", "mainnet"] ) # Contract address input contract_address = st.text_input("合約地址 (十六進制)", "0x") # Check if we have compiled contract and keys has_contract = 'compiled_contract' in st.session_state has_keys = 'keys_info' in st.session_state if not has_contract: st.warning("請先在「編寫和編譯合約」步驟中編譯合約") if not has_keys: st.warning("請先在「生成密鑰對和地址」步驟中生成密鑰") # Contract interaction section if has_contract and has_keys and st.text_input("合約地址") != "0x": st.subheader("合約交互") # Function selection function_option = st.radio( "選擇函數", ["transfer", "get_balance"] ) if function_option == "transfer": # Transfer function parameters st.subheader("轉帳參數") from_address = st.text_input("來源地址", value=st.session_state['keys_info']['address']) to_address = st.text_input("目標地址", "0x") amount = st.number_input("金額", min_value=1, value=100) if st.button("執行轉帳"): st.info("此功能在演示模式下,實際調用需要連接到 StarkNet 網絡") # Here we would implement actual StarkNet interaction st.code(f""" # 實際執行代碼將如下: client = GatewayClient(net="{network}") contract = Contract( address=int("{contract_address}", 16), abi=st.session_state['compiled_contract']['abi'], client=client ) response = await account.execute( calls=[ contract.functions["transfer"].prepare( from_address=int("{from_address}", 16), to_address=int("{to_address}", 16), amount={amount} ) ] ) """, language="python") elif function_option == "get_balance": # Get balance function parameters st.subheader("餘額查詢參數") address = st.text_input("查詢地址", value=st.session_state['keys_info']['address']) if st.button("查詢餘額"): st.info("此功能在演示模式下,實際調用需要連接到 StarkNet 網絡") # Here we would implement actual StarkNet interaction st.code(f""" # 實際執行代碼將如下: client = GatewayClient(net="{network}") contract = Contract( address=int("{contract_address}", 16), abi=st.session_state['compiled_contract']['abi'], client=client ) balance = await contract.functions["get_balance"].call( address=int("{address}", 16) ) """, language="python") # Show a mock result st.success("模擬結果: 餘額 = 1000") # Footer st.markdown("---") st.markdown("### StarkNet 開發資源") st.markdown(""" - [StarkNet 官方文檔](https://docs.starknet.io) - [Cairo 語言文檔](https://cairo-lang.org) - [StarkNet-py 庫文檔](https://github.com/software-mansion/starknet.py) """)