From d33e7a11b29e880e30aee81497277214b00baf30 Mon Sep 17 00:00:00 2001 From: Sophie Date: Fri, 15 Mar 2024 15:04:35 -0700 Subject: [PATCH 1/8] Support transpiling from Solidity --- .gitignore | 4 +- app/package-lock.json | 6 ++ app/package.json | 1 + app/src/App.tsx | 94 ++++++++++++++---- app/src/constants.ts | 26 ++++- .../editor/components/ActionOverlay.tsx | 8 +- .../editor/components/SolidityEditor.tsx | 41 ++++++++ .../components/{Editor.tsx => SwayEditor.tsx} | 10 +- app/src/features/editor/hooks/useCompile.tsx | 11 +-- .../features/editor/hooks/useTranspile.tsx | 82 ++++++++++++++++ .../toolbar/components/ActionToolbar.tsx | 14 +++ app/src/utils/localStorage.ts | 17 +++- src/compilation/mod.rs | 19 ++-- src/compilation/swaypad.rs | 17 +--- src/main.rs | 17 +++- src/transpilation/mod.rs | 95 +++++++++++++++++++ src/transpilation/solidity.rs | 13 +++ src/types.rs | 25 ++++- src/util.rs | 9 ++ 19 files changed, 446 insertions(+), 63 deletions(-) create mode 100644 app/src/features/editor/components/SolidityEditor.tsx rename app/src/features/editor/components/{Editor.tsx => SwayEditor.tsx} (79%) create mode 100644 app/src/features/editor/hooks/useTranspile.tsx create mode 100644 src/transpilation/mod.rs create mode 100644 src/transpilation/solidity.rs diff --git a/.gitignore b/.gitignore index 2368d7a..c426b01 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,6 @@ target .vercel /certs/* build -node_modules \ No newline at end of file +node_modules +tmp +.vscode/* \ No newline at end of file diff --git a/app/package-lock.json b/app/package-lock.json index bf3210f..25c54fa 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -19,6 +19,7 @@ "@mui/material": "^5.13.2", "@tanstack/react-query": "^4.24.9", "ace-builds": "^1.22.0", + "ace-mode-solidity": "^0.1.1", "ansicolor": "^1.1.100", "fuels": "^0.74", "react": "^18.2.0", @@ -6534,6 +6535,11 @@ "resolved": "https://registry.npmjs.org/ace-builds/-/ace-builds-1.32.6.tgz", "integrity": "sha512-dO5BnyDOhCnznhOpILzXq4jqkbhRXxNkf3BuVTmyxGyRLrhddfdyk6xXgy+7A8LENrcYoFi/sIxMuH3qjNUN4w==" }, + "node_modules/ace-mode-solidity": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ace-mode-solidity/-/ace-mode-solidity-0.1.1.tgz", + "integrity": "sha512-OFDYb2DpSUdY/st3o+efbBof4e3M5zFXE8p1DwXNSoeGVT5+8/3KKwX6uhkuKipZ9VgqtPDSJLNcIY1+KSsrIw==" + }, "node_modules/acorn": { "version": "8.11.3", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", diff --git a/app/package.json b/app/package.json index 0890ffc..6e0e4b4 100644 --- a/app/package.json +++ b/app/package.json @@ -14,6 +14,7 @@ "@mui/material": "^5.13.2", "@tanstack/react-query": "^4.24.9", "ace-builds": "^1.22.0", + "ace-mode-solidity": "^0.1.1", "ansicolor": "^1.1.100", "fuels": "^0.74", "react": "^18.2.0", diff --git a/app/src/App.tsx b/app/src/App.tsx index 0da1e2c..a12a96e 100644 --- a/app/src/App.tsx +++ b/app/src/App.tsx @@ -1,25 +1,43 @@ -import React, { useCallback, useState } from 'react'; -import Editor from './features/editor/components/Editor'; +import React, { useCallback, useEffect, useState } from 'react'; +import SwayEditor from './features/editor/components/SwayEditor'; import ActionToolbar from './features/toolbar/components/ActionToolbar'; import LogView from './features/editor/components/LogView'; import { useCompile } from './features/editor/hooks/useCompile'; import { DeployState } from './utils/types'; -import { loadCode, saveCode } from './utils/localStorage'; +import { + loadSolidityCode, + loadSwayCode as loadSwayCode, + saveSolidityCode, + saveSwayCode, +} from './utils/localStorage'; import InteractionDrawer from './features/interact/components/InteractionDrawer'; import { useLog } from './features/editor/hooks/useLog'; import { Toolchain } from './features/editor/components/ToolchainDropdown'; +import SolidityEditor from './features/editor/components/SolidityEditor'; +import { useTranspile } from './features/editor/hooks/useTranspile'; const DRAWER_WIDTH = '40vw'; function App() { - // The current code in the editor. - const [code, setCode] = useState(loadCode()); + // The current sway code in the editor. + const [swayCode, setSwayCode] = useState(loadSwayCode()); + + // The current solidity code in the editor. + const [solidityCode, setSolidityCode] = useState(loadSolidityCode()); + + // An error message to display to the user. + const [showSolidity, setShowSolidity] = useState(false); // The most recent code that the user has requested to compile. const [codeToCompile, setCodeToCompile] = useState( undefined ); + // The most recent code that the user has requested to transpile. + const [codeToTranspile, setCodeToTranspile] = useState( + undefined + ); + // Whether or not the current code in the editor has been compiled. const [isCompiled, setIsCompiled] = useState(false); @@ -38,13 +56,28 @@ function App() { // An error message to display to the user. const [drawerOpen, setDrawerOpen] = useState(false); - const onCodeChange = useCallback( + useEffect(() => { + if (showSolidity) { + setIsCompiled(false); + } + }, [showSolidity]); + + const onSwayCodeChange = useCallback( (code: string) => { - saveCode(code); - setCode(code); + saveSwayCode(code); + setSwayCode(code); setIsCompiled(false); }, - [setCode] + [setSwayCode] + ); + + const onSolidityCodeChange = useCallback( + (code: string) => { + saveSolidityCode(code); + setSolidityCode(code); + setIsCompiled(false); + }, + [setSolidityCode] ); const setError = useCallback( @@ -54,6 +87,16 @@ function App() { [updateLog] ); + const onCompileClick = useCallback(() => { + if (showSolidity) { + setCodeToTranspile(solidityCode); + } + if (!showSolidity) { + setCodeToCompile(swayCode); + } + }, [showSolidity, swayCode, setCodeToCompile, updateLog]); + + useTranspile(codeToTranspile, setCodeToCompile, onSwayCodeChange, setError, updateLog); useCompile(codeToCompile, setError, setIsCompiled, updateLog, toolchain); return ( @@ -66,11 +109,13 @@ function App() { setCodeToCompile(code)} + onCompile={onCompileClick} isCompiled={isCompiled} setDeployState={setDeployState} drawerOpen={drawerOpen} setDrawerOpen={setDrawerOpen} + showSolidity={showSolidity} + setShowSolidity={setShowSolidity} updateLog={updateLog} />
- +
+ {showSolidity &&
+ +
} +
+ +
+
void; - toolchain: Toolchain; - setToolchain: (toolchain: Toolchain) => void; + toolchain?: Toolchain; + setToolchain?: (toolchain: Toolchain) => void; } function ActionOverlay({ @@ -25,7 +25,7 @@ function ActionOverlay({ zIndex: 1, pointerEvents: 'none', }}> - + />}
void; +} + +function SolidityEditor({ code, onChange }: SolidityEditorProps) { + return ( +
+ onChange(DEFAULT_SOLIDITY_CONTRACT)} /> + + + +
+ ); +} + +export default SolidityEditor; diff --git a/app/src/features/editor/components/Editor.tsx b/app/src/features/editor/components/SwayEditor.tsx similarity index 79% rename from app/src/features/editor/components/Editor.tsx rename to app/src/features/editor/components/SwayEditor.tsx index 7359f57..d1e2409 100644 --- a/app/src/features/editor/components/Editor.tsx +++ b/app/src/features/editor/components/SwayEditor.tsx @@ -5,21 +5,21 @@ import 'ace-builds/src-noconflict/mode-rust'; import 'ace-builds/src-noconflict/theme-chrome'; import { StyledBorder } from '../../../components/shared'; import ActionOverlay from './ActionOverlay'; -import { DEFAULT_CONTRACT } from '../../../constants'; +import { DEFAULT_SWAY_CONTRACT } from '../../../constants'; import { Toolchain } from './ToolchainDropdown'; -export interface EditorProps { +export interface SwayEditorProps { code: string; onChange: (value: string) => void; toolchain: Toolchain; setToolchain: (toolchain: Toolchain) => void; } -function Editor({ code, onChange, toolchain, setToolchain }: EditorProps) { +function SwayEditor({ code, onChange, toolchain, setToolchain }: SwayEditorProps) { return (
onChange(DEFAULT_CONTRACT)} + handleReset={() => onChange(DEFAULT_SWAY_CONTRACT)} toolchain={toolchain} setToolchain={setToolchain} /> @@ -44,4 +44,4 @@ function Editor({ code, onChange, toolchain, setToolchain }: EditorProps) { ); } -export default Editor; +export default SwayEditor; diff --git a/app/src/features/editor/hooks/useCompile.tsx b/app/src/features/editor/hooks/useCompile.tsx index ddd775c..4d56df2 100644 --- a/app/src/features/editor/hooks/useCompile.tsx +++ b/app/src/features/editor/hooks/useCompile.tsx @@ -33,7 +33,7 @@ export function useCompile( onError: (error: string | undefined) => void, setIsCompiled: (isCompiled: boolean) => void, setResults: (entry: React.ReactElement[]) => void, - toolchain: Toolchain + toolchain: Toolchain, ) { const [serverError, setServerError] = useState(false); const [version, setVersion] = useState(); @@ -47,12 +47,11 @@ export function useCompile( setResults([<>Add some code to compile.]); return; } - - setResults([<>Compiling...]); + setResults([<>Compiling Sway contract...]); // TODO: Determine the URL based on the NODE_ENV. - const server_uri = 'https://api.sway-playground.org/compile'; - // const server_uri = 'http://0.0.0.0:8080/compile'; + // const server_uri = 'https://api.sway-playground.org/compile'; + const server_uri = 'http://0.0.0.0:8080/compile'; const request = new Request(server_uri, { method: 'POST', body: JSON.stringify({ @@ -71,7 +70,7 @@ export function useCompile( }) .then((response) => { const { error, forcVersion } = response; - if (error.length) { + if (error) { // Preserve the ANSI color codes from the compiler output. let parsedAnsi = ansicolor.parse(error); let results = parsedAnsi.spans.map((span, i) => { diff --git a/app/src/features/editor/hooks/useTranspile.tsx b/app/src/features/editor/hooks/useTranspile.tsx new file mode 100644 index 0000000..f701fe5 --- /dev/null +++ b/app/src/features/editor/hooks/useTranspile.tsx @@ -0,0 +1,82 @@ +import styled from '@emotion/styled'; +import ansicolor from 'ansicolor'; +import React, { useState, useEffect } from 'react'; +import { + saveAbi, + saveBytecode, + saveStorageSlots, +} from '../../../utils/localStorage'; + +export function useTranspile( + code: string | undefined, + setCodeToCompile: (code: string | undefined) => void, + onSwayCodeChange: (code: string) => void, + onError: (error: string | undefined) => void, + setResults: (entry: React.ReactElement[]) => void +) { + const [serverError, setServerError] = useState(false); + + useEffect(() => { + if (!code) { + return; + } + setResults([ + <> + Transpiling Solidity code with{' '} + charcoal... + , + ]); + + // TODO: Determine the URL based on the NODE_ENV. + // const server_uri = 'https://api.sway-playground.org/compile'; + const server_uri = 'http://0.0.0.0:8080/transpile'; + const request = new Request(server_uri, { + method: 'POST', + body: JSON.stringify({ + contract: code, + lanaguage: 'solidity', + }), + }); + + fetch(request) + .then((response) => { + if (response.status < 400) { + return response.json(); + } else { + setServerError(true); + } + }) + .then((response) => { + const { error, swayContract } = response; + if (error) { + // Preserve the ANSI color codes from the compiler output. + let parsedAnsi = ansicolor.parse(error); + let results = parsedAnsi.spans.map((span, i) => { + const { text, css } = span; + const Span = styled.span` + ${css} + `; + return {text}; + }); + setResults(results); + } else { + // Tell the useCompile hook to start compiling. + onSwayCodeChange(swayContract); + setCodeToCompile(swayContract); + setResults([<>Successfully transpiled Solidity contract to Sway.]); + } + }) + .catch(() => { + console.error('Unexpected error transpiling contract.'); + setServerError(true); + }); + }, [code, setResults]); + + useEffect(() => { + if (serverError) { + onError( + 'There was an unexpected error transpiling your contract. Please try again.' + ); + } + }, [serverError, onError]); +} diff --git a/app/src/features/toolbar/components/ActionToolbar.tsx b/app/src/features/toolbar/components/ActionToolbar.tsx index 21df7c9..ee08c01 100644 --- a/app/src/features/toolbar/components/ActionToolbar.tsx +++ b/app/src/features/toolbar/components/ActionToolbar.tsx @@ -19,6 +19,8 @@ export interface ActionToolbarProps { setDeployState: (state: DeployState) => void; drawerOpen: boolean; setDrawerOpen: (open: boolean) => void; + showSolidity: boolean; + setShowSolidity: (open: boolean) => void; updateLog: (entry: string) => void; } @@ -30,6 +32,8 @@ function ActionToolbar({ setDeployState, drawerOpen, setDrawerOpen, + showSolidity, + setShowSolidity, updateLog, }: ActionToolbarProps) { return ( @@ -67,6 +71,16 @@ function ActionToolbar({ : 'Interact with the contract ABI' } /> + setShowSolidity(!showSolidity)} + text='SOLIDITY' + tooltip={ + showSolidity + ? 'Hide the Solidity editor' + : 'Show the Solidity editor to transpile Solidity to Sway' + } + /> diff --git a/app/src/utils/localStorage.ts b/app/src/utils/localStorage.ts index 129b203..dcd1d65 100644 --- a/app/src/utils/localStorage.ts +++ b/app/src/utils/localStorage.ts @@ -1,9 +1,10 @@ -import { DEFAULT_CONTRACT } from '../constants'; +import { DEFAULT_SWAY_CONTRACT, DEFAULT_SOLIDITY_CONTRACT } from '../constants'; const STORAGE_ABI_KEY = 'playground_abi'; const STORAGE_SLOTS_KEY = 'playground_slots'; const STORAGE_BYTECODE_KEY = 'playground_bytecode'; const STORAGE_CONTRACT_KEY = 'playground_contract'; +const STORAGE_SOLIDITY_CONTRACT_KEY = 'playground_solidity_contract'; export function saveAbi(abi: string) { localStorage.setItem(STORAGE_ABI_KEY, abi); @@ -29,10 +30,18 @@ export function loadBytecode() { return localStorage.getItem(STORAGE_BYTECODE_KEY) || ''; } -export function saveCode(code: string) { +export function saveSwayCode(code: string) { localStorage.setItem(STORAGE_CONTRACT_KEY, code); } -export function loadCode() { - return localStorage.getItem(STORAGE_CONTRACT_KEY) ?? DEFAULT_CONTRACT; +export function saveSolidityCode(code: string) { + localStorage.setItem(STORAGE_SOLIDITY_CONTRACT_KEY, code); +} + +export function loadSwayCode() { + return localStorage.getItem(STORAGE_CONTRACT_KEY) ?? DEFAULT_SWAY_CONTRACT; +} + +export function loadSolidityCode() { + return localStorage.getItem(STORAGE_SOLIDITY_CONTRACT_KEY) ?? DEFAULT_SOLIDITY_CONTRACT; } diff --git a/src/compilation/mod.rs b/src/compilation/mod.rs index b6d37d5..411682b 100644 --- a/src/compilation/mod.rs +++ b/src/compilation/mod.rs @@ -1,14 +1,15 @@ -use crate::{types::CompileResponse, util::read_file_contents}; +mod swaypad; +mod tooling; use self::{ - swaypad::{clean_error_content, create_project, remove_project, write_main_file}, + swaypad::{ create_project, remove_project, write_main_file}, tooling::{build_project, check_forc_version, switch_fuel_toolchain}, }; +use crate::{types::CompileResponse, util::{read_file_contents, clean_error_content}}; use hex::encode; use std::fs::read_to_string; -pub mod swaypad; -pub mod tooling; +const FILE_NAME: &str = "main.sw"; /// Build and destroy a project. pub fn build_and_destroy_project(contract: String, toolchain: String) -> CompileResponse { @@ -18,8 +19,8 @@ pub fn build_and_destroy_project(contract: String, toolchain: String) -> Compile abi: "".to_string(), bytecode: "".to_string(), storage_slots: "".to_string(), - error: "No contract.".to_string(), forc_version: "".to_string(), + error: Some("No contract.".to_string()), }; } @@ -52,11 +53,11 @@ pub fn build_and_destroy_project(contract: String, toolchain: String) -> Compile // Return the abi, bin, empty error message, and forc version. CompileResponse { - abi: clean_error_content(abi), - bytecode: clean_error_content(encode(bin)), + abi: clean_error_content(abi, FILE_NAME), + bytecode: clean_error_content(encode(bin), FILE_NAME), storage_slots: String::from_utf8_lossy(&storage_slots).into(), - error: String::from(""), forc_version, + error: None, } } else { // Get the error message presented in the console output. @@ -76,7 +77,7 @@ pub fn build_and_destroy_project(contract: String, toolchain: String) -> Compile abi: String::from(""), bytecode: String::from(""), storage_slots: String::from(""), - error: clean_error_content(trunc), + error: Some(clean_error_content(trunc, FILE_NAME)), forc_version, } } diff --git a/src/compilation/swaypad.rs b/src/compilation/swaypad.rs index b69d9e6..8521f27 100644 --- a/src/compilation/swaypad.rs +++ b/src/compilation/swaypad.rs @@ -1,7 +1,7 @@ use fs_extra::dir::{copy, CopyOptions}; use nanoid::nanoid; use regex::Regex; -use std::fs::{create_dir, remove_dir_all, File}; +use std::fs::{ remove_dir_all, File, create_dir_all}; use std::io::prelude::*; const PROJECTS: &str = "projects"; @@ -12,7 +12,7 @@ pub fn create_project() -> Result { let project_name = nanoid!(); // Create a new directory for the project. - create_dir(format!("{PROJECTS}/{project_name}"))?; + create_dir_all(format!("{PROJECTS}/{project_name}"))?; // Setup the copy options for copying the template project to the new dir. let options = CopyOptions { @@ -37,20 +37,11 @@ pub fn create_project() -> Result { /// Remove a project from the projects dir. pub fn remove_project(project_name: String) -> std::io::Result<()> { - remove_dir_all(format!("{PROJECTS}/{project_name}"))?; - Ok(()) + remove_dir_all(format!("{PROJECTS}/{project_name}")) } /// Write the main sway file to a project. pub fn write_main_file(project_name: String, contract: &[u8]) -> std::io::Result<()> { let mut file = File::create(format!("{PROJECTS}/{project_name}/src/main.sw"))?; - file.write_all(contract)?; - Ok(()) -} - -/// This is a hack and should be made reletive or removed. -pub fn clean_error_content(content: String) -> std::string::String { - let path_pattern = Regex::new(r"(/).*(/main.sw)").unwrap(); - - path_pattern.replace_all(&content, "/main.sw").to_string() + file.write_all(contract) } diff --git a/src/main.rs b/src/main.rs index 8653083..4cf65e6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,15 +5,18 @@ extern crate rocket; mod compilation; mod cors; +mod transpilation; mod types; mod util; use compilation::build_and_destroy_project; use cors::Cors; use rocket::serde::json::Json; -use types::{CompileRequest, CompileResponse}; +use types::{CompileRequest, CompileResponse, TranspileRequest, Language}; -/// The compile endpoint. +use crate::{types::TranspileResponse, transpilation::solidity_to_sway}; + +/// The endpoint to compile a Sway contract. #[post("/compile", data = "")] fn compile(request: Json) -> Json { Json(build_and_destroy_project( @@ -22,6 +25,14 @@ fn compile(request: Json) -> Json { )) } +/// The endpoint to transpile a contract written in another language into Sway. +#[post("/transpile", data = "")] +fn transpile(request: Json) -> Json { + match request.lanaguage { + Language::Solidity => Json(solidity_to_sway(request.contract.to_string())), + } +} + /// Catches all OPTION requests in order to get the CORS related Fairing triggered. #[options("/<_..>")] fn all_options() { @@ -45,6 +56,6 @@ fn health() -> String { fn rocket() -> _ { rocket::build() .attach(Cors) - .mount("/", routes![compile, all_options, health]) + .mount("/", routes![compile, transpile, all_options, health]) .register("/", catchers![not_found]) } diff --git a/src/transpilation/mod.rs b/src/transpilation/mod.rs new file mode 100644 index 0000000..5e3065a --- /dev/null +++ b/src/transpilation/mod.rs @@ -0,0 +1,95 @@ +mod solidity; + +use self::solidity::run_charcoal; +use crate::{types::{CompileResponse, TranspileResponse}, util::clean_error_content}; +use nanoid::nanoid; +use regex::Regex; +use rocket::http::uri::Path; +use std::{ + fs::{create_dir_all, remove_file, File, remove_dir_all}, + io::Write, + path::PathBuf, +}; + +const TMP: &str = "tmp"; +const FILE_NAME: &str = "main.sol"; + +/// Transpile the given Solitiy contract to Sway. +pub fn solidity_to_sway(contract: String) -> TranspileResponse { + if contract.is_empty() { + return TranspileResponse { + sway_contract: "".to_string(), + error: Some("No contract.".to_string()), + }; + } + + match create_project(contract) { + Ok(project_name) => { + // Run charcoal on the file and capture the output. + let output = run_charcoal(contract_path(project_name.clone())); + let response = if !output.stderr.is_empty() { + let error: &str = std::str::from_utf8(&output.stderr).unwrap(); + TranspileResponse { + sway_contract: "".to_string(), + error: Some(clean_error_content(error.to_string(), FILE_NAME)), + } + } else if !output.stdout.is_empty() { + let result = std::str::from_utf8(&output.stdout).unwrap(); + + // Replace the generated comments from charcoal with a custom comment. + let re = Regex::new(r"// Translated from.*").unwrap(); + let replacement = "// Transpiled from Solidity using charcoal. Generated code may be incorrect or unoptimal."; + let sway_contract = re.replace_all(result, replacement).into_owned(); + + TranspileResponse { + sway_contract, + error: None, + } + } else { + TranspileResponse { + sway_contract: "".to_string(), + error: Some(format!( + "An unknown error occurred while transpiling the Solidity contract." + )), + } + }; + + // Delete the temporary file. + if let Err(err) = remove_project(project_name.clone()) { + return TranspileResponse { + sway_contract: String::from(""), + error: Some(format!("Failed to remove temporary file: {err}")), + }; + } + + response + } + Err(err) => TranspileResponse { + sway_contract: "".to_string(), + error: Some(format!("Failed to create temporary file: {err}")), + }, + } +} + +fn create_project(contract: String) -> std::io::Result { + // Create a new project file. + let project_name = nanoid!(); + create_dir_all(project_path(project_name.clone()))?; + let mut file = File::create(contract_path(project_name.clone()))?; + + // Write the contract to the file. + file.write_all(contract.as_bytes())?; + Ok(project_name) +} + +fn remove_project(project_name: String) -> std::io::Result<()> { + remove_dir_all(project_path(project_name)) +} + +fn project_path(project_name: String) -> String { + format!("{TMP}/{project_name}") +} + +fn contract_path(project_name: String) -> PathBuf { + PathBuf::from(format!("{}/{FILE_NAME}", project_path(project_name))) +} \ No newline at end of file diff --git a/src/transpilation/solidity.rs b/src/transpilation/solidity.rs new file mode 100644 index 0000000..7dd5750 --- /dev/null +++ b/src/transpilation/solidity.rs @@ -0,0 +1,13 @@ +use crate::util::spawn_and_wait; +use std::{path::PathBuf, process::{Command, Output}}; + +const CHARCOAL: &str = "charcoal"; + +/// Use forc to build the project. +pub fn run_charcoal(path: PathBuf) -> Output { + spawn_and_wait( + Command::new(CHARCOAL) + .arg("--target") + .arg(path.to_string_lossy().to_string()), + ) +} diff --git a/src/types.rs b/src/types.rs index e2c3e9d..0d84aaf 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,5 +1,11 @@ use rocket::serde::{Deserialize, Serialize}; +#[derive(Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub enum Language { + Solidity, +} + /// The compile request. #[derive(Deserialize)] pub struct CompileRequest { @@ -14,6 +20,23 @@ pub struct CompileResponse { pub abi: String, pub bytecode: String, pub storage_slots: String, - pub error: String, pub forc_version: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub error: Option, +} + +/// The transpile request. +#[derive(Deserialize, Debug)] +pub struct TranspileRequest { + pub contract: String, + pub lanaguage: Language, +} + +/// The response to a compile request. +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +pub struct TranspileResponse { + pub sway_contract: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub error: Option, } diff --git a/src/util.rs b/src/util.rs index 30b22b1..0f6e6d3 100644 --- a/src/util.rs +++ b/src/util.rs @@ -3,6 +3,8 @@ use std::io::Read; use std::path::Path; use std::process::{Command, Output, Stdio}; +use regex::Regex; + /// Check the version of forc. pub fn spawn_and_wait(cmd: &mut Command) -> Output { // Pipe stdin, stdout, and stderr to the child. @@ -41,3 +43,10 @@ pub fn read_file_contents(file_name: String) -> Vec { // Return the file's contents. file_content } + +/// This replaces the full file paths in error messages with just the file name. +pub fn clean_error_content(content: String, filename: &str) -> std::string::String { + let path_pattern = Regex::new(format!(r"(/).*(/{filename})").as_str()).unwrap(); + + path_pattern.replace_all(&content, filename).to_string() +} From c8db7d0082d942cda81de654f0db76881cadaee9 Mon Sep 17 00:00:00 2001 From: Sophie Date: Fri, 15 Mar 2024 15:23:56 -0700 Subject: [PATCH 2/8] update deps --- README.md | 10 +++++----- app/src/App.tsx | 6 +++--- app/src/constants.ts | 5 ++++- app/src/features/editor/hooks/useCompile.tsx | 7 ++----- app/src/features/editor/hooks/useTranspile.tsx | 14 +++----------- src/compilation/mod.rs | 7 +++++-- src/compilation/swaypad.rs | 3 +-- src/main.rs | 4 ++-- src/transpilation/mod.rs | 14 +++++++------- src/transpilation/solidity.rs | 5 ++++- src/types.rs | 4 ++-- src/util.rs | 3 +-- 12 files changed, 39 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index 69981cf..f9da793 100644 --- a/README.md +++ b/README.md @@ -79,13 +79,13 @@ npm start This will open http://localhost:3000 in your browser. By default, it will use the production backend endpoint. -To test against the backend running locally, make this change in `app/src/features/editor/hooks/useCompile.tsx`: +To test against the backend running locally, make this change in `app/src/constants.ts`: ```diff -- const server_uri = 'https://api.sway-playground.org/compile'; -- // const server_uri = 'http://0.0.0.0:8080/compile'; -+ // const server_uri = 'https://api.sway-playground.org/compile'; -+ const server_uri = 'http://0.0.0.0:8080/compile'; +- export const SERVER_URI = 'https://api.sway-playground.org/compile'; +- // export const SERVER_URI = 'http://0.0.0.0:8080/compile'; ++ // export const SERVER_URI = 'https://api.sway-playground.org/compile'; ++ export const SERVER_URI = 'http://0.0.0.0:8080/compile'; ``` ## Contributing to Sway diff --git a/app/src/App.tsx b/app/src/App.tsx index a12a96e..b5687e2 100644 --- a/app/src/App.tsx +++ b/app/src/App.tsx @@ -89,12 +89,12 @@ function App() { const onCompileClick = useCallback(() => { if (showSolidity) { + // Transpile the Solidity code before compiling. setCodeToTranspile(solidityCode); - } - if (!showSolidity) { + } else { setCodeToCompile(swayCode); } - }, [showSolidity, swayCode, setCodeToCompile, updateLog]); + }, [showSolidity, swayCode, solidityCode, setCodeToCompile, updateLog]); useTranspile(codeToTranspile, setCodeToCompile, onSwayCodeChange, setError, updateLog); useCompile(codeToCompile, setError, setIsCompiled, updateLog, toolchain); diff --git a/app/src/constants.ts b/app/src/constants.ts index b179a6f..5e3ae6f 100644 --- a/app/src/constants.ts +++ b/app/src/constants.ts @@ -1,5 +1,9 @@ export const FUEL_GREEN = '#00f58c'; +// TODO: Determine the URL based on the NODE_ENV. +export const SERVER_URI = 'https://api.sway-playground.org/compile'; +// export const SERVER_URI = 'http://0.0.0.0:8080/transpile'; + export const DEFAULT_SWAY_CONTRACT = `contract; abi TestContract { @@ -50,4 +54,3 @@ contract Counter { count -= 1; } }`; - diff --git a/app/src/features/editor/hooks/useCompile.tsx b/app/src/features/editor/hooks/useCompile.tsx index 4d56df2..626ee15 100644 --- a/app/src/features/editor/hooks/useCompile.tsx +++ b/app/src/features/editor/hooks/useCompile.tsx @@ -8,6 +8,7 @@ import { } from '../../../utils/localStorage'; import { CopyableHex } from '../../../components/shared'; import { Toolchain } from '../components/ToolchainDropdown'; +import { SERVER_URI } from '../../../constants'; function toResults( prefixedBytecode: string, @@ -49,10 +50,7 @@ export function useCompile( } setResults([<>Compiling Sway contract...]); - // TODO: Determine the URL based on the NODE_ENV. - // const server_uri = 'https://api.sway-playground.org/compile'; - const server_uri = 'http://0.0.0.0:8080/compile'; - const request = new Request(server_uri, { + const request = new Request(SERVER_URI, { method: 'POST', body: JSON.stringify({ contract: code, @@ -96,7 +94,6 @@ export function useCompile( } }) .catch(() => { - console.error('Unexpected error compiling contract.'); setServerError(true); }); setIsCompiled(true); diff --git a/app/src/features/editor/hooks/useTranspile.tsx b/app/src/features/editor/hooks/useTranspile.tsx index f701fe5..3cb0fc5 100644 --- a/app/src/features/editor/hooks/useTranspile.tsx +++ b/app/src/features/editor/hooks/useTranspile.tsx @@ -1,11 +1,7 @@ import styled from '@emotion/styled'; import ansicolor from 'ansicolor'; import React, { useState, useEffect } from 'react'; -import { - saveAbi, - saveBytecode, - saveStorageSlots, -} from '../../../utils/localStorage'; +import { SERVER_URI } from '../../../constants'; export function useTranspile( code: string | undefined, @@ -27,10 +23,7 @@ export function useTranspile( , ]); - // TODO: Determine the URL based on the NODE_ENV. - // const server_uri = 'https://api.sway-playground.org/compile'; - const server_uri = 'http://0.0.0.0:8080/transpile'; - const request = new Request(server_uri, { + const request = new Request(SERVER_URI, { method: 'POST', body: JSON.stringify({ contract: code, @@ -67,10 +60,9 @@ export function useTranspile( } }) .catch(() => { - console.error('Unexpected error transpiling contract.'); setServerError(true); }); - }, [code, setResults]); + }, [code, setResults, onSwayCodeChange, setCodeToCompile]); useEffect(() => { if (serverError) { diff --git a/src/compilation/mod.rs b/src/compilation/mod.rs index 411682b..f0fa64a 100644 --- a/src/compilation/mod.rs +++ b/src/compilation/mod.rs @@ -2,10 +2,13 @@ mod swaypad; mod tooling; use self::{ - swaypad::{ create_project, remove_project, write_main_file}, + swaypad::{create_project, remove_project, write_main_file}, tooling::{build_project, check_forc_version, switch_fuel_toolchain}, }; -use crate::{types::CompileResponse, util::{read_file_contents, clean_error_content}}; +use crate::{ + types::CompileResponse, + util::{clean_error_content, read_file_contents}, +}; use hex::encode; use std::fs::read_to_string; diff --git a/src/compilation/swaypad.rs b/src/compilation/swaypad.rs index 8521f27..7ba1cca 100644 --- a/src/compilation/swaypad.rs +++ b/src/compilation/swaypad.rs @@ -1,7 +1,6 @@ use fs_extra::dir::{copy, CopyOptions}; use nanoid::nanoid; -use regex::Regex; -use std::fs::{ remove_dir_all, File, create_dir_all}; +use std::fs::{create_dir_all, remove_dir_all, File}; use std::io::prelude::*; const PROJECTS: &str = "projects"; diff --git a/src/main.rs b/src/main.rs index 4cf65e6..52f48cb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,9 +12,9 @@ mod util; use compilation::build_and_destroy_project; use cors::Cors; use rocket::serde::json::Json; -use types::{CompileRequest, CompileResponse, TranspileRequest, Language}; +use types::{CompileRequest, CompileResponse, Language, TranspileRequest}; -use crate::{types::TranspileResponse, transpilation::solidity_to_sway}; +use crate::{transpilation::solidity_to_sway, types::TranspileResponse}; /// The endpoint to compile a Sway contract. #[post("/compile", data = "")] diff --git a/src/transpilation/mod.rs b/src/transpilation/mod.rs index 5e3065a..d1510a1 100644 --- a/src/transpilation/mod.rs +++ b/src/transpilation/mod.rs @@ -1,12 +1,11 @@ mod solidity; use self::solidity::run_charcoal; -use crate::{types::{CompileResponse, TranspileResponse}, util::clean_error_content}; +use crate::{types::TranspileResponse, util::clean_error_content}; use nanoid::nanoid; use regex::Regex; -use rocket::http::uri::Path; use std::{ - fs::{create_dir_all, remove_file, File, remove_dir_all}, + fs::{create_dir_all, remove_dir_all, File}, io::Write, path::PathBuf, }; @@ -40,7 +39,7 @@ pub fn solidity_to_sway(contract: String) -> TranspileResponse { let re = Regex::new(r"// Translated from.*").unwrap(); let replacement = "// Transpiled from Solidity using charcoal. Generated code may be incorrect or unoptimal."; let sway_contract = re.replace_all(result, replacement).into_owned(); - + TranspileResponse { sway_contract, error: None, @@ -48,9 +47,10 @@ pub fn solidity_to_sway(contract: String) -> TranspileResponse { } else { TranspileResponse { sway_contract: "".to_string(), - error: Some(format!( + error: Some( "An unknown error occurred while transpiling the Solidity contract." - )), + .to_string(), + ), } }; @@ -92,4 +92,4 @@ fn project_path(project_name: String) -> String { fn contract_path(project_name: String) -> PathBuf { PathBuf::from(format!("{}/{FILE_NAME}", project_path(project_name))) -} \ No newline at end of file +} diff --git a/src/transpilation/solidity.rs b/src/transpilation/solidity.rs index 7dd5750..2eb41fc 100644 --- a/src/transpilation/solidity.rs +++ b/src/transpilation/solidity.rs @@ -1,5 +1,8 @@ use crate::util::spawn_and_wait; -use std::{path::PathBuf, process::{Command, Output}}; +use std::{ + path::PathBuf, + process::{Command, Output}, +}; const CHARCOAL: &str = "charcoal"; diff --git a/src/types.rs b/src/types.rs index 0d84aaf..1c80bba 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,6 +1,6 @@ use rocket::serde::{Deserialize, Serialize}; -#[derive(Deserialize, Debug)] +#[derive(Deserialize)] #[serde(rename_all = "camelCase")] pub enum Language { Solidity, @@ -26,7 +26,7 @@ pub struct CompileResponse { } /// The transpile request. -#[derive(Deserialize, Debug)] +#[derive(Deserialize)] pub struct TranspileRequest { pub contract: String, pub lanaguage: Language, diff --git a/src/util.rs b/src/util.rs index 0f6e6d3..d0da63e 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,10 +1,9 @@ +use regex::Regex; use std::fs::File; use std::io::Read; use std::path::Path; use std::process::{Command, Output, Stdio}; -use regex::Regex; - /// Check the version of forc. pub fn spawn_and_wait(cmd: &mut Command) -> Output { // Pipe stdin, stdout, and stderr to the child. From 482c748638bc3ef2b68803850a0164ca398cef5c Mon Sep 17 00:00:00 2001 From: Sophie Date: Fri, 15 Mar 2024 16:36:30 -0700 Subject: [PATCH 3/8] resizeable --- .gitignore | 1 + README.md | 8 +-- app/src/App.tsx | 43 +++++++--------- app/src/constants.ts | 11 ++-- .../features/editor/components/EditorView.tsx | 50 +++++++++++++++++++ .../editor/components/SolidityEditor.tsx | 34 ++++++------- .../features/editor/components/SwayEditor.tsx | 41 +++++++-------- app/src/features/editor/hooks/useCompile.tsx | 4 +- .../features/editor/hooks/useTranspile.tsx | 2 +- 9 files changed, 116 insertions(+), 78 deletions(-) create mode 100644 app/src/features/editor/components/EditorView.tsx diff --git a/.gitignore b/.gitignore index c426b01..4133f23 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,5 @@ target build node_modules tmp +projects .vscode/* \ No newline at end of file diff --git a/README.md b/README.md index f9da793..ae4a30b 100644 --- a/README.md +++ b/README.md @@ -82,10 +82,10 @@ This will open http://localhost:3000 in your browser. By default, it will use th To test against the backend running locally, make this change in `app/src/constants.ts`: ```diff -- export const SERVER_URI = 'https://api.sway-playground.org/compile'; -- // export const SERVER_URI = 'http://0.0.0.0:8080/compile'; -+ // export const SERVER_URI = 'https://api.sway-playground.org/compile'; -+ export const SERVER_URI = 'http://0.0.0.0:8080/compile'; +- export const SERVER_URI = 'https://api.sway-playground.org'; +- // export const SERVER_URI = 'http://0.0.0.0:8080'; ++ // export const SERVER_URI = 'https://api.sway-playground.org'; ++ export const SERVER_URI = 'http://0.0.0.0:8080'; ``` ## Contributing to Sway diff --git a/app/src/App.tsx b/app/src/App.tsx index b5687e2..4300458 100644 --- a/app/src/App.tsx +++ b/app/src/App.tsx @@ -1,5 +1,4 @@ import React, { useCallback, useEffect, useState } from 'react'; -import SwayEditor from './features/editor/components/SwayEditor'; import ActionToolbar from './features/toolbar/components/ActionToolbar'; import LogView from './features/editor/components/LogView'; import { useCompile } from './features/editor/hooks/useCompile'; @@ -13,8 +12,8 @@ import { import InteractionDrawer from './features/interact/components/InteractionDrawer'; import { useLog } from './features/editor/hooks/useLog'; import { Toolchain } from './features/editor/components/ToolchainDropdown'; -import SolidityEditor from './features/editor/components/SolidityEditor'; import { useTranspile } from './features/editor/hooks/useTranspile'; +import EditorView from './features/editor/components/EditorView'; const DRAWER_WIDTH = '40vw'; @@ -96,7 +95,13 @@ function App() { } }, [showSolidity, swayCode, solidityCode, setCodeToCompile, updateLog]); - useTranspile(codeToTranspile, setCodeToCompile, onSwayCodeChange, setError, updateLog); + useTranspile( + codeToTranspile, + setCodeToCompile, + onSwayCodeChange, + setError, + updateLog + ); useCompile(codeToCompile, setError, setIsCompiled, updateLog, toolchain); return ( @@ -126,29 +131,15 @@ function App() { display: 'flex', flexDirection: 'column', }}> -
- {showSolidity &&
- -
} -
- -
-
+
void; + solidityCode: string; + onSolidityCodeChange: (value: string) => void; + toolchain: Toolchain; + setToolchain: (toolchain: Toolchain) => void; + showSolidity: boolean; +} + +function EditorView({ + swayCode, + solidityCode, + onSolidityCodeChange, + onSwayCodeChange, + toolchain, + setToolchain, + showSolidity, +}: EditorViewProps) { + return ( +
+ {showSolidity && ( + + )} + +
+ ); +} + +export default EditorView; diff --git a/app/src/features/editor/components/SolidityEditor.tsx b/app/src/features/editor/components/SolidityEditor.tsx index f52f8ae..3e770bf 100644 --- a/app/src/features/editor/components/SolidityEditor.tsx +++ b/app/src/features/editor/components/SolidityEditor.tsx @@ -15,26 +15,22 @@ export interface SolidityEditorProps { function SolidityEditor({ code, onChange }: SolidityEditorProps) { return ( -
+ onChange(DEFAULT_SOLIDITY_CONTRACT)} /> - - - -
+ + ); } diff --git a/app/src/features/editor/components/SwayEditor.tsx b/app/src/features/editor/components/SwayEditor.tsx index d1e2409..ed96001 100644 --- a/app/src/features/editor/components/SwayEditor.tsx +++ b/app/src/features/editor/components/SwayEditor.tsx @@ -15,32 +15,33 @@ export interface SwayEditorProps { setToolchain: (toolchain: Toolchain) => void; } -function SwayEditor({ code, onChange, toolchain, setToolchain }: SwayEditorProps) { +function SwayEditor({ + code, + onChange, + toolchain, + setToolchain, +}: SwayEditorProps) { return ( -
+ onChange(DEFAULT_SWAY_CONTRACT)} toolchain={toolchain} setToolchain={setToolchain} /> - - - -
+ + ); } diff --git a/app/src/features/editor/hooks/useCompile.tsx b/app/src/features/editor/hooks/useCompile.tsx index 626ee15..a8283e8 100644 --- a/app/src/features/editor/hooks/useCompile.tsx +++ b/app/src/features/editor/hooks/useCompile.tsx @@ -34,7 +34,7 @@ export function useCompile( onError: (error: string | undefined) => void, setIsCompiled: (isCompiled: boolean) => void, setResults: (entry: React.ReactElement[]) => void, - toolchain: Toolchain, + toolchain: Toolchain ) { const [serverError, setServerError] = useState(false); const [version, setVersion] = useState(); @@ -50,7 +50,7 @@ export function useCompile( } setResults([<>Compiling Sway contract...]); - const request = new Request(SERVER_URI, { + const request = new Request(`${SERVER_URI}/compile`, { method: 'POST', body: JSON.stringify({ contract: code, diff --git a/app/src/features/editor/hooks/useTranspile.tsx b/app/src/features/editor/hooks/useTranspile.tsx index 3cb0fc5..09bb44a 100644 --- a/app/src/features/editor/hooks/useTranspile.tsx +++ b/app/src/features/editor/hooks/useTranspile.tsx @@ -23,7 +23,7 @@ export function useTranspile( , ]); - const request = new Request(SERVER_URI, { + const request = new Request(`${SERVER_URI}/transpile`, { method: 'POST', body: JSON.stringify({ contract: code, From 1ec228a3cc7a697cadd2af62dcfdfac9949b082f Mon Sep 17 00:00:00 2001 From: Sophie Date: Fri, 15 Mar 2024 17:00:35 -0700 Subject: [PATCH 4/8] update dockerfile --- deployment/Dockerfile | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/deployment/Dockerfile b/deployment/Dockerfile index 02f1dd2..56ff538 100644 --- a/deployment/Dockerfile +++ b/deployment/Dockerfile @@ -43,8 +43,8 @@ COPY --from=builder /build/Rocket.toml . COPY --from=builder /build/projects projects # Install fuelup -RUN curl --proto '=https' --tlsv1.2 -sSf https://install.fuel.network/fuelup-init.sh | sh -s -- --no-modify-path -ENV PATH="/root/.fuelup/bin:$PATH" +RUN curl -fsSL https://install.fuel.network/ | sh -s -- --no-modify-path +ENV PATH="/root/.fuelup/bin:$HOME/.cargo/bin:$PATH" # Install all fuel toolchains RUN fuelup toolchain install latest @@ -55,6 +55,10 @@ RUN fuelup toolchain install beta-3 RUN fuelup toolchain install beta-4 RUN fuelup toolchain install beta-5 +# Install charcoal +RUN git clone https://github.com/camden-smallwood/charcoal.git +RUN cargo install --path charcoal + EXPOSE 8080 CMD ["./sway-playground"] From fda0c727998678b75d55ad841d03ae8c522aa159 Mon Sep 17 00:00:00 2001 From: Sophie Date: Fri, 15 Mar 2024 18:17:54 -0700 Subject: [PATCH 5/8] fix docker --- .gitignore | 1 - README.md | 2 +- app/src/App.tsx | 4 ++-- app/src/features/editor/components/EditorView.tsx | 2 +- deployment/Dockerfile | 15 +++++++++------ 5 files changed, 13 insertions(+), 11 deletions(-) diff --git a/.gitignore b/.gitignore index 4133f23..c426b01 100644 --- a/.gitignore +++ b/.gitignore @@ -6,5 +6,4 @@ target build node_modules tmp -projects .vscode/* \ No newline at end of file diff --git a/README.md b/README.md index ae4a30b..71a4200 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ cargo run Alternatively, it can be run locally with Docker, as it is in the deployed environment. ```sh -docker build -f deployment . +docker build -f deployment/Dockerfile . docker run -p 8080:8080 -d ``` diff --git a/app/src/App.tsx b/app/src/App.tsx index 4300458..ec6df7f 100644 --- a/app/src/App.tsx +++ b/app/src/App.tsx @@ -5,7 +5,7 @@ import { useCompile } from './features/editor/hooks/useCompile'; import { DeployState } from './utils/types'; import { loadSolidityCode, - loadSwayCode as loadSwayCode, + loadSwayCode, saveSolidityCode, saveSwayCode, } from './utils/localStorage'; @@ -93,7 +93,7 @@ function App() { } else { setCodeToCompile(swayCode); } - }, [showSolidity, swayCode, solidityCode, setCodeToCompile, updateLog]); + }, [showSolidity, swayCode, solidityCode, setCodeToCompile, setCodeToTranspile]); useTranspile( codeToTranspile, diff --git a/app/src/features/editor/components/EditorView.tsx b/app/src/features/editor/components/EditorView.tsx index e04b577..b27853a 100644 --- a/app/src/features/editor/components/EditorView.tsx +++ b/app/src/features/editor/components/EditorView.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useRef } from 'react'; +import React from 'react'; import SolidityEditor from './SolidityEditor'; import SwayEditor from './SwayEditor'; import { Toolchain } from './ToolchainDropdown'; diff --git a/deployment/Dockerfile b/deployment/Dockerfile index 56ff538..13c3d50 100644 --- a/deployment/Dockerfile +++ b/deployment/Dockerfile @@ -1,7 +1,8 @@ # Stage 1: Build -FROM lukemathwalker/cargo-chef:latest-rust-1.70 as chef +FROM lukemathwalker/cargo-chef:latest-rust-1.76 as chef WORKDIR /build/ # hadolint ignore=DL3008 + RUN apt-get update && \ apt-get install -y --no-install-recommends \ lld \ @@ -10,12 +11,17 @@ RUN apt-get update && \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* +# Build sway-playground FROM chef as planner ENV CARGO_NET_GIT_FETCH_WITH_CLI=true COPY . . RUN cargo chef prepare --recipe-path recipe.json FROM chef as builder + +# Install charcoal +RUN cargo install --git https://github.com/camden-smallwood/charcoal.git --rev f6338bf318500dd6977af813c37f66c504925c44 + ENV CARGO_NET_GIT_FETCH_WITH_CLI=true COPY --from=planner /build/recipe.json recipe.json # Build our project dependecies, not our application! @@ -41,10 +47,11 @@ COPY --from=builder /build/target/debug/sway-playground . COPY --from=builder /build/target/debug/sway-playground.d . COPY --from=builder /build/Rocket.toml . COPY --from=builder /build/projects projects +COPY --from=builder /usr/local/cargo/bin/charcoal /bin # Install fuelup RUN curl -fsSL https://install.fuel.network/ | sh -s -- --no-modify-path -ENV PATH="/root/.fuelup/bin:$HOME/.cargo/bin:$PATH" +ENV PATH="/root/.fuelup/bin:$PATH" # Install all fuel toolchains RUN fuelup toolchain install latest @@ -55,10 +62,6 @@ RUN fuelup toolchain install beta-3 RUN fuelup toolchain install beta-4 RUN fuelup toolchain install beta-5 -# Install charcoal -RUN git clone https://github.com/camden-smallwood/charcoal.git -RUN cargo install --path charcoal - EXPOSE 8080 CMD ["./sway-playground"] From 2f2601d83f11cee218e8cc12ebb9c31128cadd5f Mon Sep 17 00:00:00 2001 From: Sophie Date: Sat, 16 Mar 2024 02:27:08 -0700 Subject: [PATCH 6/8] Make app responsive on mobile --- app/src/App.tsx | 1 + app/src/components/SecondaryButton.tsx | 8 +++- .../features/editor/components/EditorView.tsx | 7 ++- .../editor/components/SolidityEditor.tsx | 10 ++++- .../toolbar/components/ActionToolbar.tsx | 44 ++++++++++--------- .../toolbar/components/CompileButton.tsx | 3 ++ app/src/hooks/useIsMobile.tsx | 8 ++++ 7 files changed, 56 insertions(+), 25 deletions(-) create mode 100644 app/src/hooks/useIsMobile.tsx diff --git a/app/src/App.tsx b/app/src/App.tsx index ec6df7f..4f04397 100644 --- a/app/src/App.tsx +++ b/app/src/App.tsx @@ -14,6 +14,7 @@ import { useLog } from './features/editor/hooks/useLog'; import { Toolchain } from './features/editor/components/ToolchainDropdown'; import { useTranspile } from './features/editor/hooks/useTranspile'; import EditorView from './features/editor/components/EditorView'; +import { useIsMobile } from './hooks/useIsMobile'; const DRAWER_WIDTH = '40vw'; diff --git a/app/src/components/SecondaryButton.tsx b/app/src/components/SecondaryButton.tsx index 2bbcbf7..f50236b 100644 --- a/app/src/components/SecondaryButton.tsx +++ b/app/src/components/SecondaryButton.tsx @@ -22,7 +22,13 @@ function SecondaryButton({ header, }: SecondaryButtonProps) { if (!!header) { - style = { ...style, minWidth: '115px', height: '40px', marginLeft: '15px' }; + style = { + ...style, + minWidth: '115px', + height: '40px', + marginRight: '15px', + marginBottom: '10px', + }; } return ( diff --git a/app/src/features/editor/components/EditorView.tsx b/app/src/features/editor/components/EditorView.tsx index b27853a..94c12bf 100644 --- a/app/src/features/editor/components/EditorView.tsx +++ b/app/src/features/editor/components/EditorView.tsx @@ -2,6 +2,7 @@ import React from 'react'; import SolidityEditor from './SolidityEditor'; import SwayEditor from './SwayEditor'; import { Toolchain } from './ToolchainDropdown'; +import { useIsMobile } from '../../../hooks/useIsMobile'; export interface EditorViewProps { swayCode: string; @@ -22,16 +23,18 @@ function EditorView({ setToolchain, showSolidity, }: EditorViewProps) { + const isMobile = useIsMobile(); + return (
{showSolidity && ( diff --git a/app/src/features/editor/components/SolidityEditor.tsx b/app/src/features/editor/components/SolidityEditor.tsx index 3e770bf..7d4ab45 100644 --- a/app/src/features/editor/components/SolidityEditor.tsx +++ b/app/src/features/editor/components/SolidityEditor.tsx @@ -7,6 +7,7 @@ import { StyledBorder } from '../../../components/shared'; import 'ace-mode-solidity/build/remix-ide/mode-solidity'; import ActionOverlay from './ActionOverlay'; import { DEFAULT_SOLIDITY_CONTRACT } from '../../../constants'; +import { useIsMobile } from '../../../hooks/useIsMobile'; export interface SolidityEditorProps { code: string; @@ -14,8 +15,15 @@ export interface SolidityEditorProps { } function SolidityEditor({ code, onChange }: SolidityEditorProps) { + const isMobile = useIsMobile(); + return ( - + onChange(DEFAULT_SOLIDITY_CONTRACT)} /> { + window.open('https://docs.fuel.network/docs/sway', '_blank', 'noreferrer'); + }, []); + return (
- + {!isMobile && ( + + )} setDrawerOpen(!drawerOpen)} @@ -83,13 +91,7 @@ function ActionToolbar({ /> - window.open( - 'https://docs.fuel.network/docs/sway', - '_blank', - 'noreferrer' - ) - } + onClick={onDocsClick} text='DOCS' tooltip={'Open documentation for Sway in a new tab'} endIcon={} diff --git a/app/src/features/toolbar/components/CompileButton.tsx b/app/src/features/toolbar/components/CompileButton.tsx index c5fcbe7..5b51c45 100644 --- a/app/src/features/toolbar/components/CompileButton.tsx +++ b/app/src/features/toolbar/components/CompileButton.tsx @@ -26,6 +26,9 @@ function CompileButton({ sx={{ ...style, height: '40px', + marginRight: '15px', + width: '115px', + marginBottom: '10px', background: lightColors.scalesGreen7, borderColor: darkColors.gray6, color: darkColors.gray6, diff --git a/app/src/hooks/useIsMobile.tsx b/app/src/hooks/useIsMobile.tsx new file mode 100644 index 0000000..af66d4f --- /dev/null +++ b/app/src/hooks/useIsMobile.tsx @@ -0,0 +1,8 @@ +import React from 'react'; +import useTheme from "@mui/material/styles/useTheme"; +import useMediaQuery from "@mui/material/useMediaQuery/useMediaQuery"; + +export function useIsMobile() { + const theme = useTheme(); + return useMediaQuery(theme.breakpoints.down('md')); +} From 0b3d49370a5706ffdffcf6573a546fdb83ad42be Mon Sep 17 00:00:00 2001 From: Sophie Date: Sat, 16 Mar 2024 02:31:43 -0700 Subject: [PATCH 7/8] Interact button --- .../toolbar/components/ActionToolbar.tsx | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/app/src/features/toolbar/components/ActionToolbar.tsx b/app/src/features/toolbar/components/ActionToolbar.tsx index 5610e6b..d5378e5 100644 --- a/app/src/features/toolbar/components/ActionToolbar.tsx +++ b/app/src/features/toolbar/components/ActionToolbar.tsx @@ -68,17 +68,19 @@ function ActionToolbar({ updateLog={updateLog} /> )} - setDrawerOpen(!drawerOpen)} - text='INTERACT' - disabled={deployState !== DeployState.DEPLOYED} - tooltip={ - deployState !== DeployState.DEPLOYED - ? 'A contract must be deployed to interact with it on-chain' - : 'Interact with the contract ABI' - } - /> + {!isMobile && ( + setDrawerOpen(!drawerOpen)} + text='INTERACT' + disabled={deployState !== DeployState.DEPLOYED} + tooltip={ + deployState !== DeployState.DEPLOYED + ? 'A contract must be deployed to interact with it on-chain' + : 'Interact with the contract ABI' + } + /> + )} setShowSolidity(!showSolidity)} From 8e526d65a9463cfff5517198bca133ef9705c889 Mon Sep 17 00:00:00 2001 From: Sophie Date: Sat, 16 Mar 2024 02:32:30 -0700 Subject: [PATCH 8/8] lint --- app/src/App.tsx | 9 +++++++-- app/src/hooks/useIsMobile.tsx | 9 ++++----- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/app/src/App.tsx b/app/src/App.tsx index 4f04397..15d6f0c 100644 --- a/app/src/App.tsx +++ b/app/src/App.tsx @@ -14,7 +14,6 @@ import { useLog } from './features/editor/hooks/useLog'; import { Toolchain } from './features/editor/components/ToolchainDropdown'; import { useTranspile } from './features/editor/hooks/useTranspile'; import EditorView from './features/editor/components/EditorView'; -import { useIsMobile } from './hooks/useIsMobile'; const DRAWER_WIDTH = '40vw'; @@ -94,7 +93,13 @@ function App() { } else { setCodeToCompile(swayCode); } - }, [showSolidity, swayCode, solidityCode, setCodeToCompile, setCodeToTranspile]); + }, [ + showSolidity, + swayCode, + solidityCode, + setCodeToCompile, + setCodeToTranspile, + ]); useTranspile( codeToTranspile, diff --git a/app/src/hooks/useIsMobile.tsx b/app/src/hooks/useIsMobile.tsx index af66d4f..7eb3587 100644 --- a/app/src/hooks/useIsMobile.tsx +++ b/app/src/hooks/useIsMobile.tsx @@ -1,8 +1,7 @@ -import React from 'react'; -import useTheme from "@mui/material/styles/useTheme"; -import useMediaQuery from "@mui/material/useMediaQuery/useMediaQuery"; +import useTheme from '@mui/material/styles/useTheme'; +import useMediaQuery from '@mui/material/useMediaQuery/useMediaQuery'; export function useIsMobile() { - const theme = useTheme(); - return useMediaQuery(theme.breakpoints.down('md')); + const theme = useTheme(); + return useMediaQuery(theme.breakpoints.down('md')); }