CoolFace
Apppublic

fugthchat/Vivara-Forge

sourceHugging Facemitupdated 10mo agoView on Hugging Face
0likes
app.py738 linesDownload Raw Back to root
1import streamlit as st2from solcx import compile_source, install_solc3import json4import streamlit.components.v1 as components5 6# --- PAGE CONFIG ---7st.set_page_config(8    layout="wide",9    page_title="Vivara Forge",10    page_icon="๐Ÿฆ",11    initial_sidebar_state="collapsed",12)13 14# --- HIDE ALL STREAMLIT UI ---15st.markdown(16    """17<style>18    #MainMenu, header, footer, [data-testid="stToolbar"], [data-testid="stDecoration"], 19    [data-testid="stStatusWidget"], .stDeployButton, div[data-testid="stSidebarNav"],20    section[data-testid="stSidebar"], .viewerBadge_container__1QSob,21    [data-testid="stHeader"], [data-testid="stFooter"] { 22        display: none !important; 23        visibility: hidden !important;24    }25    .stApp { background: #1e1e1e !important; }26    .block-container { padding: 0 !important; max-width: 100% !important; }27    [data-testid="stAppViewContainer"] { padding: 0 !important; }28    29    /* Hide the form completely */30    .stForm { position: absolute; left: -9999px; }31</style>32""",33    unsafe_allow_html=True,34)35 36# --- SESSION STATE ---37if "code" not in st.session_state:38    st.session_state.code = """// SPDX-License-Identifier: MIT39pragma solidity ^0.8.20;40 41contract MyToken {42    string public name = "Vivara Coin";43    string public symbol = "VIVR";44    uint8 public decimals = 18;45    uint256 public totalSupply;46    47    mapping(address => uint256) public balanceOf;48    mapping(address => mapping(address => uint256)) public allowance;49    50    event Transfer(address indexed from, address indexed to, uint256 value);51    event Approval(address indexed owner, address indexed spender, uint256 value);52    53    constructor(uint256 _initialSupply) {54        totalSupply = _initialSupply * 10 ** decimals;55        balanceOf[msg.sender] = totalSupply;56        emit Transfer(address(0), msg.sender, totalSupply);57    }58    59    function transfer(address to, uint256 amount) public returns (bool) {60        require(balanceOf[msg.sender] >= amount, "Insufficient balance");61        balanceOf[msg.sender] -= amount;62        balanceOf[to] += amount;63        emit Transfer(msg.sender, to, amount);64        return true;65    }66    67    function approve(address spender, uint256 amount) public returns (bool) {68        allowance[msg.sender][spender] = amount;69        emit Approval(msg.sender, spender, amount);70        return true;71    }72    73    function transferFrom(address from, address to, uint256 amount) public returns (bool) {74        require(balanceOf[from] >= amount, "Insufficient balance");75        require(allowance[from][msg.sender] >= amount, "Allowance exceeded");76        allowance[from][msg.sender] -= amount;77        balanceOf[from] -= amount;78        balanceOf[to] += amount;79        emit Transfer(from, to, amount);80        return true;81    }82}"""83 84if "abi" not in st.session_state:85    st.session_state.abi = None86if "bytecode" not in st.session_state:87    st.session_state.bytecode = None88if "contract_name" not in st.session_state:89    st.session_state.contract_name = None90if "logs" not in st.session_state:91    st.session_state.logs = []92if "compile_trigger" not in st.session_state:93    st.session_state.compile_trigger = False94 95 96# --- COMPILE FUNCTION ---97def do_compile():98    try:99        st.session_state.logs.append("[INFO] Installing solc 0.8.20...")100        install_solc("0.8.20")101 102        st.session_state.logs.append("[INFO] Compiling Solidity code...")103        compiled = compile_source(104            st.session_state.code, output_values=["abi", "bin"], solc_version="0.8.20"105        )106 107        contract_id, interface = list(compiled.items())[-1]108        st.session_state.abi = interface["abi"]109        st.session_state.bytecode = interface["bin"]110        st.session_state.contract_name = contract_id.split(":")[-1]111 112        st.session_state.logs.append(113            f"[SUCCESS] Compiled: {st.session_state.contract_name}"114        )115        return True116    except Exception as e:117        st.session_state.logs.append(f"[ERROR] {str(e)}")118        return False119 120 121# Check for compile trigger122if st.session_state.compile_trigger:123    do_compile()124    st.session_state.compile_trigger = False125 126# --- FULL REMIX-LIKE HTML APP ---127abi_json = json.dumps(st.session_state.abi) if st.session_state.abi else "null"128bytecode = st.session_state.bytecode or ""129contract_name = st.session_state.contract_name or "No Contract"130logs_html = (131    "\\n".join(st.session_state.logs[-15:])132    if st.session_state.logs133    else "[INFO] Vivara Forge Ready - Connect MetaMask to deploy"134)135 136# Escape the code for JS137code_escaped = (138    st.session_state.code.replace("\\", "\\\\")139    .replace("`", "\\`")140    .replace("${", "\\${")141)142 143html = f"""144<!DOCTYPE html>145<html>146<head>147    <meta charset="UTF-8">148    <link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;600&family=Inter:wght@400;600&display=swap" rel="stylesheet">149    <script src="https://cdn.ethers.io/lib/ethers-5.7.umd.min.js"></script>150    <style>151        * {{ margin: 0; padding: 0; box-sizing: border-box; }}152        body {{ 153            font-family: 'Inter', sans-serif; 154            background: #1e1e1e; 155            color: #d4d4d4; 156            height: 100vh; 157            display: flex; 158            flex-direction: column;159            overflow: hidden;160        }}161        162        /* HEADER */163        .header {{164            height: 44px;165            background: linear-gradient(180deg, #2d2d2d 0%, #252526 100%);166            border-bottom: 1px solid #3c3c3c;167            display: flex;168            align-items: center;169            padding: 0 16px;170            gap: 12px;171        }}172        .logo {{ 173            font-family: 'JetBrains Mono'; 174            font-weight: 600; 175            font-size: 14px;176            color: #fff;177            display: flex;178            align-items: center;179            gap: 8px;180        }}181        .logo .dot {{ width: 10px; height: 10px; background: #3fc04f; border-radius: 50%; box-shadow: 0 0 8px #3fc04f; }}182        183        .header-btn {{184            background: #0e639c;185            color: white;186            border: none;187            padding: 7px 16px;188            border-radius: 4px;189            cursor: pointer;190            font-size: 12px;191            font-weight: 600;192            display: flex;193            align-items: center;194            gap: 6px;195            transition: all 0.15s;196        }}197        .header-btn:hover {{ background: #1177bb; transform: translateY(-1px); }}198        .header-btn.orange {{ background: linear-gradient(180deg, #e8850f 0%, #d4730f 100%); }}199        .header-btn.orange:hover {{ background: linear-gradient(180deg, #f59622 0%, #e8850f 100%); }}200        .header-btn.green {{ background: linear-gradient(180deg, #3ab55f 0%, #2ea44f 100%); }}201        .header-btn.green:hover {{ background: linear-gradient(180deg, #4cc76f 0%, #3ab55f 100%); }}202        203        .wallet-status {{204            margin-left: auto;205            display: flex;206            align-items: center;207            gap: 12px;208            font-size: 12px;209        }}210        .wallet-badge {{211            background: #333;212            padding: 5px 12px;213            border-radius: 20px;214            font-family: 'JetBrains Mono';215            font-size: 11px;216            border: 1px solid #444;217        }}218        .wallet-badge.connected {{ border-color: #3fc04f; color: #3fc04f; }}219        220        /* MAIN LAYOUT */221        .main {{ flex: 1; display: flex; min-height: 0; }}222        223        /* LEFT SIDEBAR */224        .sidebar {{225            width: 240px;226            background: #252526;227            border-right: 1px solid #3c3c3c;228            display: flex;229            flex-direction: column;230            overflow-y: auto;231        }}232        .sidebar-section {{ padding: 14px; border-bottom: 1px solid #3c3c3c; }}233        .sidebar-title {{234            font-size: 11px;235            text-transform: uppercase;236            color: #858585;237            margin-bottom: 10px;238            letter-spacing: 1px;239            font-weight: 600;240        }}241        .file-item {{242            display: flex;243            align-items: center;244            gap: 8px;245            padding: 8px 10px;246            cursor: pointer;247            border-radius: 4px;248            font-size: 13px;249            margin-bottom: 2px;250        }}251        .file-item:hover {{ background: #2a2d2e; }}252        .file-item.active {{ background: #37373d; border-left: 2px solid #0e639c; }}253        254        .env-select {{255            width: 100%;256            background: #3c3c3c;257            border: 1px solid #555;258            color: #d4d4d4;259            padding: 10px;260            border-radius: 6px;261            font-size: 12px;262            margin-bottom: 12px;263            cursor: pointer;264        }}265        .env-select:focus {{ border-color: #0e639c; outline: none; }}266        267        .input-group {{ margin-bottom: 12px; }}268        .input-group label {{269            display: block;270            font-size: 11px;271            color: #858585;272            margin-bottom: 6px;273            text-transform: uppercase;274        }}275        .input-group input {{276            width: 100%;277            background: #3c3c3c;278            border: 1px solid #555;279            color: #d4d4d4;280            padding: 8px 10px;281            border-radius: 6px;282            font-size: 12px;283            font-family: 'JetBrains Mono';284        }}285        .input-group input:focus {{ border-color: #0e639c; outline: none; }}286        287        /* EDITOR AREA */288        .editor-container {{ flex: 1; display: flex; flex-direction: column; min-width: 0; }}289        290        .tab-bar {{291            height: 38px;292            background: #252526;293            display: flex;294            align-items: flex-end;295            border-bottom: 1px solid #3c3c3c;296            padding-left: 8px;297        }}298        .tab {{299            padding: 8px 20px;300            display: flex;301            align-items: center;302            gap: 8px;303            font-size: 13px;304            cursor: pointer;305            border: 1px solid transparent;306            border-bottom: none;307            margin-bottom: -1px;308            border-radius: 6px 6px 0 0;309            background: #2d2d2d;310        }}311        .tab.active {{ 312            background: #1e1e1e; 313            border-color: #3c3c3c;314            border-top: 2px solid #0e639c;315        }}316        .tab .close {{ 317            opacity: 0.5; 318            font-size: 16px;319            margin-left: 4px;320        }}321        .tab .close:hover {{ opacity: 1; }}322        323        .editor {{324            flex: 1;325            display: flex;326            background: #1e1e1e;327            overflow: hidden;328        }}329        .line-numbers {{330            width: 55px;331            background: #1e1e1e;332            border-right: 1px solid #333;333            padding: 14px 10px;334            text-align: right;335            font-family: 'JetBrains Mono';336            font-size: 13px;337            color: #6e7681;338            line-height: 1.6;339            user-select: none;340        }}341        .code-area {{342            flex: 1;343            padding: 14px 16px;344            font-family: 'JetBrains Mono';345            font-size: 13px;346            line-height: 1.6;347            overflow: auto;348            white-space: pre;349            color: #d4d4d4;350        }}351        .keyword {{ color: #569cd6; }}352        .string {{ color: #ce9178; }}353        .comment {{ color: #6a9955; font-style: italic; }}354        .number {{ color: #b5cea8; }}355        .type {{ color: #4ec9b0; }}356        .function-name {{ color: #dcdcaa; }}357        358        /* RIGHT PANEL */359        .right-panel {{360            width: 320px;361            background: #252526;362            border-left: 1px solid #3c3c3c;363            display: flex;364            flex-direction: column;365        }}366        .panel-header {{367            padding: 14px;368            border-bottom: 1px solid #3c3c3c;369            font-weight: 600;370            font-size: 14px;371            display: flex;372            align-items: center;373            gap: 8px;374        }}375        .panel-content {{ flex: 1; padding: 14px; overflow-y: auto; }}376        377        .deploy-btn {{378            width: 100%;379            background: linear-gradient(180deg, #e8850f 0%, #d4730f 100%);380            color: white;381            border: none;382            padding: 14px;383            border-radius: 6px;384            cursor: pointer;385            font-size: 14px;386            font-weight: 600;387            margin-bottom: 14px;388            display: flex;389            align-items: center;390            justify-content: center;391            gap: 8px;392            transition: all 0.15s;393        }}394        .deploy-btn:hover {{ transform: translateY(-1px); box-shadow: 0 4px 12px rgba(212, 115, 15, 0.3); }}395        .deploy-btn:disabled {{ background: #555; cursor: not-allowed; transform: none; box-shadow: none; }}396        397        .info-card {{398            background: #1e1e1e;399            padding: 12px;400            border-radius: 6px;401            margin-bottom: 14px;402            border: 1px solid #333;403        }}404        .info-card .row {{405            display: flex;406            justify-content: space-between;407            margin-bottom: 6px;408            font-size: 12px;409        }}410        .info-card .row:last-child {{ margin-bottom: 0; }}411        .info-card .label {{ color: #858585; }}412        .info-card .value {{ font-family: 'JetBrains Mono'; color: #4ec9b0; }}413        414        .contract-card {{415            background: #1e1e1e;416            padding: 10px;417            border-radius: 6px;418            margin-bottom: 8px;419            border: 1px solid #333;420            font-size: 11px;421        }}422        .contract-card .name {{ color: #4ec9b0; font-weight: 600; margin-bottom: 4px; }}423        .contract-card .address {{ color: #858585; font-family: 'JetBrains Mono'; word-break: break-all; }}424        425        /* TERMINAL */426        .terminal-container {{427            height: 160px;428            background: #1e1e1e;429            border-top: 1px solid #3c3c3c;430            display: flex;431            flex-direction: column;432        }}433        .terminal-header {{434            padding: 8px 14px;435            background: #252526;436            border-bottom: 1px solid #3c3c3c;437            font-size: 12px;438            display: flex;439            align-items: center;440            gap: 8px;441        }}442        .terminal {{443            flex: 1;444            font-family: 'JetBrains Mono';445            font-size: 12px;446            padding: 10px 14px;447            overflow-y: auto;448            color: #d4d4d4;449            line-height: 1.5;450        }}451        .terminal .error {{ color: #f14c4c; }}452        .terminal .success {{ color: #3fc04f; }}453        .terminal .info {{ color: #858585; }}454        455        /* STATUS BAR */456        .status-bar {{457            height: 26px;458            background: #007acc;459            display: flex;460            align-items: center;461            padding: 0 14px;462            font-size: 12px;463            gap: 20px;464        }}465        .status-item {{ display: flex; align-items: center; gap: 6px; }}466    </style>467</head>468<body>469    <div class="header">470        <div class="logo"><div class="dot"></div> VIVARA FORGE</div>471        <button class="header-btn green" onclick="connectWallet()">๐ŸฆŠ Connect Wallet</button>472        <button class="header-btn orange" onclick="compileContract()">โšก Compile</button>473        <div class="wallet-status">474            <span id="networkBadge" class="wallet-badge">Not Connected</span>475            <span id="addressBadge" class="wallet-badge">0x...</span>476        </div>477    </div>478    479    <div class="main">480        <div class="sidebar">481            <div class="sidebar-section">482                <div class="sidebar-title">๐Ÿ“ File Explorer</div>483                <div class="file-item active">๐Ÿ“„ Contract.sol</div>484                <div class="file-item">๐Ÿ“„ Token.sol</div>485                <div class="file-item">๐Ÿ“ artifacts</div>486            </div>487            488            <div class="sidebar-section">489                <div class="sidebar-title">โš™๏ธ Deploy Environment</div>490                <select class="env-select" id="envSelect">491                    <option value="metamask">๐ŸฆŠ Injected - MetaMask</option>492                    <option value="sepolia">๐Ÿ”— Sepolia Testnet</option>493                    <option value="polygon">๐ŸŸฃ Polygon Mumbai</option>494                </select>495                496                <div class="input-group">497                    <label>Account</label>498                    <input type="text" id="accountInput" value="Connect wallet..." readonly>499                </div>500                501                <div class="input-group">502                    <label>Gas Limit</label>503                    <input type="text" id="gasLimit" value="3000000">504                </div>505                506                <div class="input-group">507                    <label>Value (ETH)</label>508                    <input type="text" id="valueEth" value="0">509                </div>510            </div>511            512            <div class="sidebar-section">513                <div class="sidebar-title">๐Ÿ”ง Compiler</div>514                <select class="env-select" id="compilerVersion">515                    <option value="0.8.20">Solidity v0.8.20</option>516                    <option value="0.8.19">Solidity v0.8.19</option>517                    <option value="0.8.18">Solidity v0.8.18</option>518                </select>519            </div>520        </div>521        522        <div class="editor-container">523            <div class="tab-bar">524                <div class="tab active">๐Ÿ“„ Contract.sol <span class="close">ร—</span></div>525            </div>526            <div class="editor">527                <div class="line-numbers" id="lineNumbers"></div>528                <div class="code-area" id="codeDisplay"></div>529            </div>530            <div class="terminal-container">531                <div class="terminal-header">๐Ÿ“Ÿ Terminal</div>532                <div class="terminal" id="terminal"></div>533            </div>534        </div>535        536        <div class="right-panel">537            <div class="panel-header">๐Ÿš€ Deploy & Run</div>538            <div class="panel-content">539                <div class="info-card">540                    <div class="row">541                        <span class="label">Contract</span>542                        <span class="value" id="contractName">{contract_name}</span>543                    </div>544                    <div class="row">545                        <span class="label">Status</span>546                        <span class="value" id="compileStatus">{"โœ… Compiled" if st.session_state.bytecode else "โณ Not Compiled"}</span>547                    </div>548                    <div class="row">549                        <span class="label">Bytecode</span>550                        <span class="value">{len(bytecode) if bytecode else 0} bytes</span>551                    </div>552                </div>553                554                <div class="input-group">555                    <label>Constructor Arguments</label>556                    <input type="text" id="constructorArgs" placeholder="e.g., 1000000">557                </div>558                559                <button class="deploy-btn" id="deployBtn" onclick="deployContract()" {"" if st.session_state.bytecode else "disabled"}>560                    ๐Ÿš€ Deploy Contract561                </button>562                563                <div class="sidebar-title" style="margin-top: 20px;">Deployed Contracts</div>564                <div id="contractsList"></div>565            </div>566        </div>567    </div>568    569    <div class="status-bar">570        <div class="status-item">๐Ÿฆ Vivara Forge v2.0.1</div>571        <div class="status-item" id="statusNetwork">Network: --</div>572        <div class="status-item" id="statusBalance">Balance: --</div>573    </div>574    575    <script>576        const contractABI = {abi_json};577        const contractBytecode = "{bytecode}";578        579        let provider = null;580        let signer = null;581        let userAddress = null;582        583        const code = `{code_escaped}`;584        585        function highlightCode(code) {{586            return code587                .replace(/&/g, '&amp;')588                .replace(/</g, '&lt;')589                .replace(/>/g, '&gt;')590                .replace(/(\/\/.*$)/gm, '<span class="comment">$1</span>')591                .replace(/(".*?"|'.*?')/g, '<span class="string">$1</span>')592                .replace(/\\b(pragma|solidity|contract|function|public|private|external|internal|view|pure|payable|returns|return|if|else|for|while|mapping|event|emit|require|constructor|import|is|memory|storage|calldata|indexed|modifier|override|virtual)\\b/g, '<span class="keyword">$1</span>')593                .replace(/\\b(address|uint256|uint8|uint128|int256|string|bool|bytes|bytes32|bytes4)\\b/g, '<span class="type">$1</span>')594                .replace(/\\b(\\d+)\\b/g, '<span class="number">$1</span>');595        }}596        597        function updateEditor() {{598            const lines = code.split('\\n');599            document.getElementById('lineNumbers').innerHTML = lines.map((_, i) => i + 1).join('<br>');600            document.getElementById('codeDisplay').innerHTML = highlightCode(code);601        }}602        603        function log(msg, type = '') {{604            const terminal = document.getElementById('terminal');605            const time = new Date().toLocaleTimeString();606            terminal.innerHTML += `<div class="${{type}}">[${time}] ${msg}</div>`;607            terminal.scrollTop = terminal.scrollHeight;608        }}609        610        async function connectWallet() {{611            if (!window.ethereum) {{612                log('โŒ MetaMask not detected! Please install MetaMask extension.', 'error');613                alert('MetaMask not found! Please install MetaMask browser extension.');614                return;615            }}616            617            try {{618                log('Connecting to MetaMask...', 'info');619                provider = new ethers.providers.Web3Provider(window.ethereum);620                await provider.send("eth_requestAccounts", []);621                signer = provider.getSigner();622                userAddress = await signer.getAddress();623                624                const network = await provider.getNetwork();625                const balance = await provider.getBalance(userAddress);626                const balanceEth = parseFloat(ethers.utils.formatEther(balance)).toFixed(4);627                628                document.getElementById('addressBadge').textContent = userAddress.slice(0,6) + '...' + userAddress.slice(-4);629                document.getElementById('addressBadge').classList.add('connected');630                document.getElementById('networkBadge').textContent = network.name || 'Chain ' + network.chainId;631                document.getElementById('networkBadge').classList.add('connected');632                document.getElementById('accountInput').value = userAddress.slice(0,12) + '...';633                document.getElementById('statusNetwork').textContent = 'Network: ' + (network.name || network.chainId);634                document.getElementById('statusBalance').textContent = 'Balance: ' + balanceEth + ' ETH';635                636                log('โœ… Wallet connected: ' + userAddress, 'success');637                log('Network: ' + (network.name || 'Chain ' + network.chainId), 'info');638                log('Balance: ' + balanceEth + ' ETH', 'info');639            }} catch (e) {{640                log('โŒ Connection failed: ' + e.message, 'error');641            }}642        }}643        644        function compileContract() {{645            log('โšก Sending compile request to server...', 'info');646            log('This will compile with Solidity 0.8.20', 'info');647            648            // Trigger Streamlit rerun with compile flag649            const url = new URL(window.parent.location.href);650            url.searchParams.set('compile', 'true');651            window.parent.location.href = url.toString();652        }}653        654        async function deployContract() {{655            if (!signer) {{656                log('โŒ Please connect your wallet first!', 'error');657                alert('Please connect MetaMask first!');658                return;659            }}660            661            if (!contractBytecode || contractBytecode === '') {{662                log('โŒ Contract not compiled! Click Compile first.', 'error');663                alert('Please compile the contract first!');664                return;665            }}666            667            try {{668                const btn = document.getElementById('deployBtn');669                btn.disabled = true;670                btn.innerHTML = 'โณ Deploying...';671                672                log('๐Ÿš€ Starting deployment...', 'info');673                674                const factory = new ethers.ContractFactory(contractABI, contractBytecode, signer);675                676                const argsInput = document.getElementById('constructorArgs').value.trim();677                let args = [];678                if (argsInput) {{679                    args = argsInput.split(',').map(a => {{680                        const trimmed = a.trim();681                        if (/^\\d+$/.test(trimmed)) return trimmed;682                        return trimmed;683                    }});684                }}685                686                log('Constructor args: [' + (args.length ? args.join(', ') : 'none') + ']', 'info');687                688                const contract = await factory.deploy(...args);689                log('๐Ÿ“ค Transaction sent: ' + contract.deployTransaction.hash, 'info');690                log('โณ Waiting for confirmation...', 'info');691                692                await contract.deployed();693                694                log('โœ… CONTRACT DEPLOYED!', 'success');695                log('๐Ÿ“ Address: ' + contract.address, 'success');696                697                const list = document.getElementById('contractsList');698                list.innerHTML += `699                    <div class="contract-card">700                        <div class="name">${{document.getElementById('contractName').textContent}}</div>701                        <div class="address">${{contract.address}}</div>702                    </div>703                `;704                705                btn.disabled = false;706                btn.innerHTML = '๐Ÿš€ Deploy Contract';707                708            }} catch (e) {{709                log('โŒ Deployment failed: ' + e.message, 'error');710                document.getElementById('deployBtn').disabled = false;711                document.getElementById('deployBtn').innerHTML = '๐Ÿš€ Deploy Contract';712            }}713        }}714        715        // Initialize716        updateEditor();717        log('Vivara Forge v2.0.1 Ready', 'info');718        log('Connect MetaMask and compile to deploy contracts', 'info');719        720        // Auto-connect if MetaMask already authorized721        if (window.ethereum && window.ethereum.selectedAddress) {{722            setTimeout(connectWallet, 500);723        }}724    </script>725</body>726</html>727"""728 729# Check URL params for compile trigger730query_params = st.query_params731if query_params.get("compile") == "true":732    do_compile()733    # Clear the param734    st.query_params.clear()735 736# Render full-screen HTML IDE737components.html(html, height=850, scrolling=False)738