diff --git a/packages/frontend/src/components/Sidebar.tsx b/packages/frontend/src/components/Sidebar.tsx
index 758573b..cb6339e 100644
--- a/packages/frontend/src/components/Sidebar.tsx
+++ b/packages/frontend/src/components/Sidebar.tsx
@@ -8,7 +8,8 @@ import { store } from "@/state";
// import ContractExplorer from "./ContractExplorer";
import dynamic from "next/dynamic";
-const ContractExplorer = dynamic(() => import("./ContractExplorer"), { ssr: false });
+const CompileExplorer = dynamic(() => import("./CompileExplorer"), { ssr: false });
+const DeployExplorer = dynamic(() => import("./DeployExplorer"), { ssr: false });
function Sidebar() {
const { sidebar } = useAppStore();
@@ -18,13 +19,18 @@ function Sidebar() {
return
;
}
- if (sidebar === SidebarView.CONTRACT) {
- return
;
+ if (sidebar === SidebarView.COMPILE) {
+ return
;
+ }
+
+ if (sidebar === SidebarView.DEPLOY) {
+ return
;
}
return (
+
);
}
diff --git a/packages/frontend/src/hooks/useCompile.tsx b/packages/frontend/src/hooks/useCompile.tsx
new file mode 100644
index 0000000..6b163a8
--- /dev/null
+++ b/packages/frontend/src/hooks/useCompile.tsx
@@ -0,0 +1,96 @@
+import { EditorContext } from "@/context/EditorProvider";
+import { useFileContent } from "@/state/hooks";
+import { useSelector } from "@xstate/store/react";
+import { useContext } from "react";
+import { store } from "@/state";
+import { logger } from "@/state/utils";
+import { Keypair, Networks } from "@stellar/stellar-sdk";
+import generateIdl from "@/lib/idl-wasm";
+import deployStellerContract from "@/lib/deploy-steller";
+
+export interface ICompilationResult {
+ data: null | Buffer,
+ err: null | string
+}
+
+function useCompile() {
+ const code = useFileContent();
+ const selected = useSelector(store, (state) => state.context.currentFile);
+
+ const compileFile = async (): Promise
=> {
+ console.log('[tur] [compileFile] code:', code)
+ if (!code) {
+ const err ="Error: No Source Code Found"
+ logger.error(err);
+ return {
+ data: null,
+ err
+ }
+ }
+
+ logger.info("Compiling contract...");
+
+ const opts: RequestInit = {
+ method: "POST",
+ mode: "cors",
+ credentials: "same-origin",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({
+ source: code,
+ }),
+ };
+
+ const { result, success, message } = await fetch("/compile", opts).then(async (res) => {
+ console.log(res);
+ console.log(store);
+ const result = await res.json().catch(() => null);
+
+ if (!result) {
+ return {
+ success: false,
+ message: res.statusText,
+ result: null,
+ };
+ }
+
+ return {
+ success: res.ok,
+ message: res.statusText,
+ result: result,
+ };
+ });
+
+ let err = "";
+
+ if (success) {
+ if (result.type === "SUCCESS") {
+ const wasm = result.payload.wasm;
+ store.send({ type: "updateCurrentWasm", path: selected || '', buff: wasm });
+ logger.info("Contract compiled successfully!");
+ return {
+ data: wasm,
+ err: null
+ };
+ } else {
+ const message = result.payload.compile_stderr;
+ logger.error(message);
+ err = message
+ }
+ } else {
+ logger.error(message);
+ err = message
+ }
+ console.log('[tur] compilatiion error:', err)
+ return {
+ data: null,
+ err
+ }
+ }
+
+ return {
+ compileFile
+ }
+
+}
+
+export default useCompile;
diff --git a/packages/frontend/src/hooks/useDeploy.tsx b/packages/frontend/src/hooks/useDeploy.tsx
new file mode 100644
index 0000000..b608adf
--- /dev/null
+++ b/packages/frontend/src/hooks/useDeploy.tsx
@@ -0,0 +1,56 @@
+
+import { EditorContext } from "@/context/EditorProvider";
+import { useFileContent } from "@/state/hooks";
+import { useSelector } from "@xstate/store/react";
+import { useContext } from "react";
+import { store } from "@/state";
+import { logger } from "@/state/utils";
+import { Keypair, Networks } from "@stellar/stellar-sdk";
+import generateIdl from "@/lib/idl-wasm";
+import deployStellerContract from "@/lib/deploy-steller";
+import { FunctionSpec } from "@/types/idl";
+import useCompile from "./useCompile";
+
+
+function useDeploy() {
+ const {compileFile} = useCompile();
+ const selected = useSelector(store, (state) => state.context.currentFile);
+ const currWasm = useSelector(store, (state) => state.context.currentWasm);
+
+
+ const deployWasm = async (contract: null | Buffer) => {
+ console.log('[tur] deploying', contract)
+
+ if(currWasm.path.indexOf(selected || '') > -1) {
+ contract = currWasm.buff
+ } else if(!contract && selected && selected !== 'explorer') {
+ const r = await compileFile();
+ contract = r.data
+ }
+
+ if (!contract) {
+ return;
+ }
+ try {
+ const keypair = Keypair.random();
+
+ logger.info("Deploying contract...");
+ const idl = await generateIdl(contract);
+ const fltrd = idl.filter((i: FunctionSpec) => i.name.indexOf('constructor') == -1);
+ store.send({ type: "updateContract", methods: fltrd });
+ const contractAddress = await deployStellerContract(contract, keypair, Networks.TESTNET);
+ logger.info("Contract deployed successfully!");
+ contractAddress && store.send({ type: "updateContract", address: contractAddress });
+
+ } catch {
+ return !1
+ }
+ return !0
+ }
+
+ return {
+ deployWasm
+ }
+}
+
+export default useDeploy;
\ No newline at end of file
diff --git a/packages/frontend/src/lib/utils.ts b/packages/frontend/src/lib/utils.ts
index 5b19755..abcf8d0 100644
--- a/packages/frontend/src/lib/utils.ts
+++ b/packages/frontend/src/lib/utils.ts
@@ -9,7 +9,7 @@ export const downloadBlob = (code: number[]): void => {
const blob = new Blob([new Uint8Array(code).buffer]);
const a = document.createElement("a");
- a.download = "result.contract";
+ a.download = "result.wasm";
a.href = URL.createObjectURL(blob);
a.dataset.downloadurl = ["application/json", a.download, a.href].join(":");
a.style.display = "none";
diff --git a/packages/frontend/src/pages/_error.js b/packages/frontend/src/pages/_error.js
new file mode 100644
index 0000000..cc05f55
--- /dev/null
+++ b/packages/frontend/src/pages/_error.js
@@ -0,0 +1,16 @@
+function Error({ statusCode }) {
+ return (
+
+ {statusCode
+ ? `An error ${statusCode} occurred on server`
+ : 'An error occurred on client'}
+
+ )
+ }
+
+ Error.getInitialProps = ({ res, err }) => {
+ const statusCode = res ? res.statusCode : err ? err.statusCode : 404
+ return { statusCode }
+ }
+
+ export default Error
\ No newline at end of file
diff --git a/packages/frontend/src/state/context.ts b/packages/frontend/src/state/context.ts
index f5b335c..83859d0 100644
--- a/packages/frontend/src/state/context.ts
+++ b/packages/frontend/src/state/context.ts
@@ -2,6 +2,7 @@ import { ExpNodeType, FolderType } from "@/types/explorer";
import { LogType } from "@/types/log";
import { Monaco } from "@monaco-editor/react";
import { Contract, IDL } from "@/types/idl";
+import { ICompiled, ICurrentWasm } from "@/types/contracts";
export * from "@stellar/stellar-sdk";
export * as contract from "@stellar/stellar-sdk/contract";
export * as rpc from "@stellar/stellar-sdk/rpc";
@@ -37,7 +38,17 @@ export const context = {
invoking: false,
address: null,
methods: [],
+ // for deploy explorer's deployed instance list
+ // to invoke functions therein
+ deployed: {},
} as Contract,
+ // for deploy explorer's drop down list
+ // if the list has something, we can select one and press deploy btn
+ compiled: [] as ICompiled[],
+ currentWasm: {
+ path: '',
+ buff: null,
+ } as ICurrentWasm,
};
export type Context = typeof context;
diff --git a/packages/frontend/src/state/events.ts b/packages/frontend/src/state/events.ts
index 1f78669..7a9ef73 100644
--- a/packages/frontend/src/state/events.ts
+++ b/packages/frontend/src/state/events.ts
@@ -7,7 +7,8 @@ import { Monaco } from "@monaco-editor/react";
import { createPath } from "./utils";
import { MessageType } from "vscode-languageserver-protocol";
import { nanoid } from "nanoid";
-import { Contract, IDL } from "@/types/idl";
+import { Contract, ContractsDeployed, IDL } from "@/types/idl";
+import { ICompiled, ICurrentWasm } from "@/types/contracts";
export const events = {
toggleFolder: (context: Context, event: { path: string }) => {
@@ -36,6 +37,7 @@ export const events = {
}
}
},
+ addDeployedContract(context: Context, event: { basePath: string; name: string, contract: any }) {},
addFile(context: Context, event: { basePath: string; name: string; content: string }) {
const path = createPath(event.basePath, event.name);
const file = {
@@ -65,6 +67,7 @@ export const events = {
} satisfies FolderType;
},
+
deleteFile(context: Context, event: { path: string; basePath: string }) {
const folder = get(context, event.basePath) as FolderType;
const file = get(context, event.path) as FileType;
@@ -72,6 +75,7 @@ export const events = {
events.removeTab(context, { path: event.path });
delete folder.items[file.name];
delete context.files[event.path];
+ context.compiled = context.compiled.filter(c => c.path !== event.path)
},
deleteFolder(context: Context, event: { path: string }) {
unset(context, event.path);
@@ -130,6 +134,32 @@ export const events = {
// context.contract.methods = event.idl;
// },
updateContract(context: Context, event: Partial) {
+ const addr = event.address;
+ if(addr && Object.keys(context.contract.deployed).indexOf(addr || '') == -1) {
+ context.contract.deployed[addr] = context.contract.methods
+ }
Object.assign(context.contract, event);
},
+
+ deleteDeployed(context: Context, event: {addr: string}) {
+ console.log('[tur] remove deployed:', event.addr);
+ const copy = { ...context.contract.deployed };
+ console.log('[tur] copy:', copy, copy[event.addr]);
+
+ delete copy[event.addr];
+ context.contract.deployed = copy;
+
+ },
+
+ addCompiled(context: Context, event: Partial) {
+ const d = {} as ICompiled;
+ Object.assign(d, event);
+ const x = context.compiled.filter(c => c.path == event.path);
+ if(x.length == 0) context.compiled.push(d);
+ },
+
+ updateCurrentWasm(context: Context, event: Partial) {
+ console.log('[tur] event updateCurrentWasm:', event)
+ Object.assign(context.currentWasm, event)
+ }
};
diff --git a/packages/frontend/src/types/contracts.ts b/packages/frontend/src/types/contracts.ts
new file mode 100644
index 0000000..d0b3686
--- /dev/null
+++ b/packages/frontend/src/types/contracts.ts
@@ -0,0 +1,9 @@
+export interface ICompiled {
+ path: string,
+ name: string,
+}
+
+export interface ICurrentWasm {
+ path: string,
+ buff: Buffer | null,
+}
\ No newline at end of file
diff --git a/packages/frontend/src/types/idl.ts b/packages/frontend/src/types/idl.ts
index fb4b79e..5add947 100644
--- a/packages/frontend/src/types/idl.ts
+++ b/packages/frontend/src/types/idl.ts
@@ -8,6 +8,10 @@ export interface FunctionSpec {
outputs: OutputSpec[];
}
+export interface ContractsDeployed {
+ [key: string]: IDL;
+}
+
interface InputSpec {
doc: string;
name: string;
@@ -38,4 +42,5 @@ export interface Contract {
address: string | null;
methods: IDL;
invoking: boolean;
+ deployed: ContractsDeployed;
}
diff --git a/solidity/counter_demo.abi b/solidity/counter_demo.abi
new file mode 100644
index 0000000..26e5c03
--- /dev/null
+++ b/solidity/counter_demo.abi
@@ -0,0 +1 @@
+[{"name":"count","type":"function","inputs":[],"outputs":[{"name":"count","type":"uint64","internalType":"uint64"}],"stateMutability":"view"},{"name":"decrement","type":"function","inputs":[],"outputs":[{"name":"","type":"uint64","internalType":"uint64"}],"stateMutability":"nonpayable"}]
\ No newline at end of file
diff --git a/solidity/counter_demo.sol b/solidity/counter_demo.sol
new file mode 100755
index 0000000..cb2b489
--- /dev/null
+++ b/solidity/counter_demo.sol
@@ -0,0 +1,12 @@
+contract counter_demo {
+
+
+ uint64 public count = 1;
+
+ function decrement() public returns (uint64){
+ count -= 1;
+ return count;
+ }
+
+
+}
\ No newline at end of file
diff --git a/solidity/counter_demo.wasm b/solidity/counter_demo.wasm
new file mode 100644
index 0000000..61d8543
Binary files /dev/null and b/solidity/counter_demo.wasm differ
diff --git a/solidity/hello_world.wasm b/solidity/hello_world.wasm
new file mode 100755
index 0000000..f7232c0
Binary files /dev/null and b/solidity/hello_world.wasm differ
diff --git a/solidity/soroban_token_contract.wasm b/solidity/soroban_token_contract.wasm
new file mode 100755
index 0000000..2be33cb
Binary files /dev/null and b/solidity/soroban_token_contract.wasm differ
diff --git a/sysbox/on-start.sh b/sysbox/on-start.sh
index 06b05a1..1bc2fbd 100755
--- a/sysbox/on-start.sh
+++ b/sysbox/on-start.sh
@@ -5,7 +5,6 @@ dockerd > /var/log/dockerd.log 2>&1 &
sleep 2
# pull solang image
-docker pull ghcr.io/hyperledger/solang@sha256:8776a9bd756664f7bf8414710d1a799799bf6fedc1c8f9f0bda17e76749dea7a
+docker pull ghcr.io/hyperledger/solang@sha256:e6f687910df5dd9d4f5285aed105ae0e6bcae912db43e8955ed4d8344d49785d
-# start backend server
-./app/target/release/backend --port 9000 --host 0.0.0.0 --frontend_folder /app/packages/app/dist
+cargo make run