fugthchat commited on
Commit
0bf353d
Β·
verified Β·
1 Parent(s): 2c7fac5

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +112 -106
app.py CHANGED
@@ -6,16 +6,16 @@ import json
6
  import pandas as pd
7
  import firebase_admin
8
  from firebase_admin import credentials, firestore
 
9
 
10
  # --- PAGE CONFIG ---
11
  st.set_page_config(page_title="Vivara Forge", page_icon="🦁", layout="wide")
12
 
13
- # --- SESSION STATE SETUP ---
14
  if 'is_creator' not in st.session_state:
15
  st.session_state['is_creator'] = False
16
 
17
- # --- 1. FIREBASE CONNECTION (Backend) ---
18
- # This allows the app to talk to your database
19
  if not firebase_admin._apps:
20
  key_json = os.getenv("FIREBASE_SERVICE_ACCOUNT")
21
  if key_json:
@@ -24,65 +24,49 @@ if not firebase_admin._apps:
24
  cred = credentials.Certificate(cred_dict)
25
  firebase_admin.initialize_app(cred)
26
  except Exception as e:
27
- st.warning("⚠️ Firebase Key Error. Admin features disabled.")
28
  else:
29
- st.warning("⚠️ Admin Database Not Connected (Add FIREBASE_SERVICE_ACCOUNT secret)")
30
 
31
  try:
32
  db = firestore.client()
33
  except:
34
  db = None
35
 
36
- # --- SIDEBAR: LOGIN & WALLET ---
37
  with st.sidebar:
38
  st.header("πŸ‘€ Identity")
39
-
40
- # LOGIN FORM
41
  if not st.session_state['is_creator']:
42
  with st.expander("Creator Login"):
43
- admin_pass = st.text_input("Admin Password", type="password")
44
- if st.button("Unlock Creator Mode"):
45
- # Check against the secret you saved in Hugging Face
46
- if admin_pass == os.getenv("ADMIN_PASSWORD"):
47
  st.session_state['is_creator'] = True
48
- st.success("Welcome back, Fugth.")
49
  st.rerun()
50
  else:
51
  st.error("Wrong Password")
52
  else:
53
- st.success("🟒 CREATOR MODE ACTIVE")
54
  if st.button("Logout"):
55
  st.session_state['is_creator'] = False
56
  st.rerun()
57
 
58
  st.divider()
59
-
60
- st.header("βš™οΈ Network Settings")
61
- rpc_url = st.text_input("RPC URL", value="https://polygon-rpc.com")
62
  chain_id = st.number_input("Chain ID", value=137)
63
-
64
- # Wallet Status
65
- pk = os.getenv("PRIVATE_KEY")
66
- if pk:
67
- st.info("πŸ”‘ Admin Wallet Loaded")
68
- else:
69
- st.warning("⚠️ No Wallet Key Found")
70
-
71
- # --- MAIN APP LOGIC ---
72
-
73
- # IF CREATOR: Show Everything (3 Tabs)
74
- # IF PUBLIC: Show Only Forge (1 Tab)
75
 
 
76
  if st.session_state['is_creator']:
77
  st.title("🦁 Vivara Command Center")
78
- tabs = st.tabs(["βš’οΈ Solidity Forge", "πŸ‘₯ User Database", "πŸ’° Coin & Stats"])
79
  else:
80
  st.title("βš’οΈ Vivara Forge (Public)")
81
- st.caption("Open Source Smart Contract Deployment Tool")
82
  tabs = st.tabs(["βš’οΈ Solidity Forge"])
83
 
84
  # ==========================================
85
- # TAB 1: SOLIDITY FORGE (PUBLIC)
86
  # ==========================================
87
  with tabs[0]:
88
  col1, col2 = st.columns([1.5, 1])
@@ -97,104 +81,126 @@ contract MyToken is ERC20 {
97
  constructor() ERC20("MyToken", "MTK") {
98
  _mint(msg.sender, 1000 * 10**18);
99
  }
100
- }
101
- """
102
- code_input = st.text_area("Solidity Code", value=default_code, height=500)
103
 
104
  with col2:
105
- st.subheader("πŸš€ Compiler")
106
- if st.button("COMPILE"):
107
- with st.spinner("Installing Compiler..."):
108
  try:
109
  install_solc('0.8.20')
110
  compiled = compile_source(code_input, output_values=['abi', 'bin'], solc_version='0.8.20')
111
  contract_id, iface = list(compiled.items())[-1]
112
  st.session_state['abi'] = iface['abi']
113
  st.session_state['bin'] = iface['bin']
 
114
  st.success(f"βœ… Compiled: {contract_id}")
115
  except Exception as e:
116
  st.error(f"Error: {e}")
117
-
118
  st.divider()
119
 
120
- # DEPLOY BUTTON (Only enabled if Creator logged in OR use Metamask injection trick)
121
- # Since this is Python-based, usually only the Admin deploys via the server key.
122
- if st.session_state['is_creator']:
123
- if st.button("DEPLOY (Admin Wallet)"):
124
- if not pk:
125
- st.error("No Private Key!")
126
- else:
127
- try:
128
- w3 = Web3(Web3.HTTPProvider(rpc_url))
129
- acct = w3.eth.account.from_key(pk)
130
- Contract = w3.eth.contract(abi=st.session_state['abi'], bytecode=st.session_state['bin'])
131
- tx = Contract.constructor().build_transaction({
132
- 'from': acct.address,
133
- 'nonce': w3.eth.get_transaction_count(acct.address),
134
- 'gas': 3000000,
135
- 'gasPrice': w3.to_wei('50', 'gwei'),
136
- 'chainId': int(chain_id)
137
- })
138
- signed = w3.eth.account.sign_transaction(tx, pk)
139
- tx_hash = w3.eth.send_raw_transaction(signed.rawTransaction)
140
- st.success(f"πŸš€ Deployed! TX: {w3.to_hex(tx_hash)}")
141
- except Exception as e:
142
- st.error(f"Deploy Error: {e}")
143
- else:
144
- st.info("πŸ”’ Server-side deployment is restricted to Admins. Public deployment via MetaMask coming soon.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
145
 
146
  # ==========================================
147
- # TAB 2: USER DATABASE (CREATOR ONLY)
148
  # ==========================================
149
  if st.session_state['is_creator']:
150
  with tabs[1]:
151
- st.header("πŸ‘₯ User Management")
152
  if db:
153
- users_ref = db.collection("users")
154
- all_users = list(users_ref.stream())
155
-
156
- founders = 0
157
- alphas = 0
158
-
159
  data = []
160
- for doc in all_users:
161
- u = doc.to_dict()
162
- inv = u.get("inventory", [])
163
-
164
  tier = "Free"
165
- if "FOUNDER" in inv:
166
- tier = "πŸ† GOLD"
167
- founders += 1
168
- elif "ALPHA" in inv:
169
  tier = "πŸ₯ˆ SILVER"
170
- alphas += 1
171
-
172
- data.append({
173
- "Name": u.get("displayName", "Unknown"),
174
- "Email": u.get("email", "-"),
175
- "Tier": tier,
176
- "Rank": u.get("redeem_rank", "-")
177
- })
178
-
179
- k1, k2, k3 = st.columns(3)
180
- k1.metric("Total Users", len(all_users))
181
- k2.metric("Founders", founders)
182
- k3.metric("Revenue", f"${(founders + alphas) * 5}")
183
 
 
184
  st.dataframe(pd.DataFrame(data), use_container_width=True)
185
 
186
- # NEWSLETTER EXPORT
187
- st.subheader("πŸ“§ Newsletter List")
188
- news_docs = list(db.collection("newsletters").stream())
189
- emails = [d.to_dict().get("email") for d in news_docs]
190
- st.write(f"collected {len(emails)} emails")
191
- st.download_button("Download CSV", pd.DataFrame(emails, columns=["Email"]).to_csv(), "emails.csv")
 
 
 
 
192
 
193
- # ==========================================
194
- # TAB 3: COIN & STATS (CREATOR ONLY)
195
- # ==========================================
196
- if st.session_state['is_creator']:
197
  with tabs[2]:
198
- st.header("πŸ’° Vivara Coin Stats")
199
- # You can add coin burning logic here later
200
- st.info("Connect Token Contract to see live burn stats.")
 
 
 
 
 
6
  import pandas as pd
7
  import firebase_admin
8
  from firebase_admin import credentials, firestore
9
+ import streamlit.components.v1 as components
10
 
11
  # --- PAGE CONFIG ---
12
  st.set_page_config(page_title="Vivara Forge", page_icon="🦁", layout="wide")
13
 
14
+ # --- SESSION STATE ---
15
  if 'is_creator' not in st.session_state:
16
  st.session_state['is_creator'] = False
17
 
18
+ # --- 1. FIREBASE CONNECTION ---
 
19
  if not firebase_admin._apps:
20
  key_json = os.getenv("FIREBASE_SERVICE_ACCOUNT")
21
  if key_json:
 
24
  cred = credentials.Certificate(cred_dict)
25
  firebase_admin.initialize_app(cred)
26
  except Exception as e:
27
+ st.warning("⚠️ Firebase Error. Admin features disabled.")
28
  else:
29
+ st.warning("⚠️ Admin DB Not Connected.")
30
 
31
  try:
32
  db = firestore.client()
33
  except:
34
  db = None
35
 
36
+ # --- SIDEBAR ---
37
  with st.sidebar:
38
  st.header("πŸ‘€ Identity")
 
 
39
  if not st.session_state['is_creator']:
40
  with st.expander("Creator Login"):
41
+ pwd = st.text_input("Password", type="password")
42
+ if st.button("Unlock"):
43
+ if pwd == os.getenv("ADMIN_PASSWORD"):
 
44
  st.session_state['is_creator'] = True
 
45
  st.rerun()
46
  else:
47
  st.error("Wrong Password")
48
  else:
49
+ st.success("πŸ‘‘ CREATOR MODE")
50
  if st.button("Logout"):
51
  st.session_state['is_creator'] = False
52
  st.rerun()
53
 
54
  st.divider()
55
+ st.header("βš™οΈ Network")
56
+ rpc_url = st.text_input("RPC URL", "https://polygon-rpc.com")
 
57
  chain_id = st.number_input("Chain ID", value=137)
 
 
 
 
 
 
 
 
 
 
 
 
58
 
59
+ # --- MAIN LOGIC ---
60
  if st.session_state['is_creator']:
61
  st.title("🦁 Vivara Command Center")
62
+ tabs = st.tabs(["βš’οΈ Solidity Forge", "πŸ‘₯ User DB", "πŸ“§ Newsletter"])
63
  else:
64
  st.title("βš’οΈ Vivara Forge (Public)")
65
+ st.caption("Open Source Smart Contract Studio")
66
  tabs = st.tabs(["βš’οΈ Solidity Forge"])
67
 
68
  # ==========================================
69
+ # TAB 1: SOLIDITY FORGE (PUBLIC & ADMIN)
70
  # ==========================================
71
  with tabs[0]:
72
  col1, col2 = st.columns([1.5, 1])
 
81
  constructor() ERC20("MyToken", "MTK") {
82
  _mint(msg.sender, 1000 * 10**18);
83
  }
84
+ }"""
85
+ code_input = st.text_area("Code", value=default_code, height=500)
 
86
 
87
  with col2:
88
+ st.subheader("πŸš€ Actions")
89
+ if st.button("1. COMPILE"):
90
+ with st.spinner("Compiling..."):
91
  try:
92
  install_solc('0.8.20')
93
  compiled = compile_source(code_input, output_values=['abi', 'bin'], solc_version='0.8.20')
94
  contract_id, iface = list(compiled.items())[-1]
95
  st.session_state['abi'] = iface['abi']
96
  st.session_state['bin'] = iface['bin']
97
+ st.session_state['name'] = contract_id
98
  st.success(f"βœ… Compiled: {contract_id}")
99
  except Exception as e:
100
  st.error(f"Error: {e}")
101
+
102
  st.divider()
103
 
104
+ # DEPLOYMENT OPTIONS
105
+ if 'abi' in st.session_state:
106
+ st.write(f"**Target:** {st.session_state['name']}")
107
+
108
+ # OPTION A: PUBLIC (MetaMask Injection)
109
+ abi_str = json.dumps(st.session_state['abi'])
110
+ bin_str = st.session_state['bin']
111
+
112
+ html_code = f"""
113
+ <html>
114
+ <head><script src="https://cdn.ethers.io/lib/ethers-5.2.umd.min.js"></script></head>
115
+ <body>
116
+ <button onclick="deploy()" style="background:#f6851b; color:white; border:none; padding:10px 20px; border-radius:5px; font-weight:bold; cursor:pointer; width:100%;">🦊 DEPLOY WITH METAMASK</button>
117
+ <div id="log" style="margin-top:10px; font-family:monospace; color:white; font-size:12px;"></div>
118
+ <script>
119
+ async function deploy() {{
120
+ const log = document.getElementById("log");
121
+ if (!window.ethereum) {{ log.innerText = "MetaMask not found!"; return; }}
122
+ try {{
123
+ log.innerText = "Connecting...";
124
+ const provider = new ethers.providers.Web3Provider(window.ethereum);
125
+ await provider.send("eth_requestAccounts", []);
126
+ const signer = provider.getSigner();
127
+ const factory = new ethers.ContractFactory({abi_str}, "{bin_str}", signer);
128
+ log.innerText = "Please confirm in Wallet...";
129
+ const contract = await factory.deploy();
130
+ log.innerText = "Sent! Hash: " + contract.deployTransaction.hash;
131
+ await contract.deployed();
132
+ log.innerText = "βœ… Deployed: " + contract.address;
133
+ }} catch (e) {{ log.innerText = "Error: " + e.message; }}
134
+ }}
135
+ </script>
136
+ </body></html>
137
+ """
138
+ components.html(html_code, height=150)
139
+
140
+ # OPTION B: ADMIN (Server Wallet)
141
+ if st.session_state['is_creator']:
142
+ st.caption("--- OR ---")
143
+ if st.button("πŸ€– DEPLOY WITH ADMIN KEY"):
144
+ pk = os.getenv("PRIVATE_KEY")
145
+ if not pk:
146
+ st.error("No Private Key in Secrets")
147
+ else:
148
+ try:
149
+ w3 = Web3(Web3.HTTPProvider(rpc_url))
150
+ acct = w3.eth.account.from_key(pk)
151
+ Contract = w3.eth.contract(abi=st.session_state['abi'], bytecode=st.session_state['bin'])
152
+ tx = Contract.constructor().build_transaction({
153
+ 'from': acct.address,
154
+ 'nonce': w3.eth.get_transaction_count(acct.address),
155
+ 'gas': 3000000,
156
+ 'gasPrice': w3.to_wei('50', 'gwei'),
157
+ 'chainId': int(chain_id)
158
+ })
159
+ signed = w3.eth.account.sign_transaction(tx, pk)
160
+ tx_hash = w3.eth.send_raw_transaction(signed.rawTransaction)
161
+ st.success(f"πŸš€ Admin Deploy: {w3.to_hex(tx_hash)}")
162
+ except Exception as e:
163
+ st.error(f"Admin Error: {e}")
164
 
165
  # ==========================================
166
+ # TAB 2 & 3: CREATOR TOOLS
167
  # ==========================================
168
  if st.session_state['is_creator']:
169
  with tabs[1]:
170
+ st.header("πŸ‘₯ User Database")
171
  if db:
172
+ users = list(db.collection("users").stream())
 
 
 
 
 
173
  data = []
174
+ founders = 0
175
+ for u in users:
176
+ d = u.to_dict()
 
177
  tier = "Free"
178
+ if "FOUNDER" in d.get("inventory", []):
179
+ tier = "πŸ† GOLD"; founders += 1
180
+ elif "ALPHA" in d.get("inventory", []):
 
181
  tier = "πŸ₯ˆ SILVER"
182
+
183
+ data.append({"Name": d.get("displayName"), "Email": d.get("email"), "Tier": tier, "Rank": d.get("redeem_rank")})
 
 
 
 
 
 
 
 
 
 
 
184
 
185
+ st.metric("Total Founders", founders, f"${founders*5} Revenue")
186
  st.dataframe(pd.DataFrame(data), use_container_width=True)
187
 
188
+ # Manual Grant
189
+ st.divider()
190
+ email_grant = st.text_input("Grant Founder to Email:")
191
+ if st.button("GRANT"):
192
+ q = db.collection("users").where("email", "==", email_grant).limit(1).get()
193
+ if q:
194
+ q[0].reference.update({"inventory": firestore.ArrayUnion(["FOUNDER", "LICENSE_V1", "MANUAL_GRANT"])})
195
+ st.success("Granted!")
196
+ else:
197
+ st.error("User not found.")
198
 
 
 
 
 
199
  with tabs[2]:
200
+ st.header("πŸ“§ Newsletter")
201
+ if db:
202
+ news = list(db.collection("newsletters").stream())
203
+ emails = [{"Email": d.to_dict().get("email"), "Date": d.to_dict().get("timestamp")} for d in news]
204
+ df = pd.DataFrame(emails)
205
+ st.dataframe(df, use_container_width=True)
206
+ st.download_button("Download CSV", df.to_csv(), "emails.csv")