Spaces:
Sleeping
Sleeping
| 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) | |
| """) |