diff --git a/media/tools.svg b/media/tools.svg
new file mode 100644
index 0000000..f1cd568
--- /dev/null
+++ b/media/tools.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/package.json b/package.json
index c0dcbb5..b154772 100644
--- a/package.json
+++ b/package.json
@@ -2,7 +2,7 @@
"name": "sui-simulator-vscode",
"displayName": "sui-simulator-vscode",
"description": "",
- "version": "0.0.1",
+ "version": "0.2.0",
"engines": {
"vscode": "^1.87.0"
},
@@ -12,6 +12,26 @@
"activationEvents": [],
"main": "./dist/extension.js",
"contributes": {
+ "viewsContainers": {
+ "activitybar": [
+ {
+ "id": "sui-simulator-sidebar-view",
+ "title": "Sui Simulator",
+ "icon": "media/tools.svg"
+ }
+ ]
+ },
+ "views": {
+ "sui-simulator-sidebar-view": [
+ {
+ "type": "webview",
+ "id": "sui-simulator-sidebar",
+ "name": "Sui Simulator",
+ "icon": "media/tools.svg",
+ "contextualTitle": "Sui Simulator"
+ }
+ ]
+ },
"commands": [
{
"command": "sui-simulator-vscode.helloWorld",
@@ -75,4 +95,4 @@
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^5.0.4"
}
-}
+}
\ No newline at end of file
diff --git a/src/SidebarProvider.ts b/src/SidebarProvider.ts
new file mode 100644
index 0000000..e0d6fe8
--- /dev/null
+++ b/src/SidebarProvider.ts
@@ -0,0 +1,79 @@
+import * as vscode from "vscode";
+import { build, executeCommand, publish } from "./suiCommand";
+import { join } from "path";
+
+export class SidebarProvider implements vscode.WebviewViewProvider {
+ constructor(private readonly _extensionContext: vscode.ExtensionContext) { }
+
+ public resolveWebviewView(webviewView: vscode.WebviewView, context: vscode.WebviewViewResolveContext, token: vscode.CancellationToken) {
+
+ webviewView.webview.options = {
+ // Allow scripts in the webview
+ enableScripts: true,
+
+ localResourceRoots: [this._extensionContext.extensionUri],
+ };
+
+ webviewView.webview.html = this._getHtmlForWebview(webviewView.webview);
+
+ webviewView.webview.onDidReceiveMessage((message) => {
+ const { command, requestId, payload } = message;
+
+ switch (command) {
+ case "SUI_TERMINAL":
+ executeCommand(payload.command, payload.suiPath);
+ break;
+
+ case "BUILD":
+ build(payload.packagePath, payload.suiPath);
+ break;
+
+ case "PUBLISH":
+ publish(payload.packagePath, payload.suiPath);
+ break;
+
+ case "SAVE_ALIASES":
+ this._extensionContext.workspaceState.update(payload.address, {
+ aliases: payload.aliases
+ }).then(() => {
+ vscode.window.showInformationMessage("Aliases saved successfully!");
+ });
+
+ // use value as undefined to remove the key
+ // context.workspaceState.update("", undefined);
+
+ default:
+ vscode.window.showInformationMessage(`Unknown command: ${command}`);
+ }
+ });
+ }
+
+ private _getHtmlForWebview(webview: vscode.Webview) {
+ const jsFile = "webview.js";
+ const localServerUrl = "http://localhost:9999";
+
+ let scriptUrl = null;
+ let cssUrl = null;
+
+ const isProduction = this._extensionContext.extensionMode === vscode.ExtensionMode.Production;
+ if (isProduction) {
+ scriptUrl = webview.asWebviewUri(vscode.Uri.file(join(this._extensionContext.extensionPath, 'dist', jsFile))).toString();
+ } else {
+ scriptUrl = `${localServerUrl}/${jsFile}`;
+ }
+
+ return `
+
+
+
+
+ ${isProduction ? `` : ''}
+
+
+
+
+
+
+ `;
+ }
+}
\ No newline at end of file
diff --git a/src/enums/index.ts b/src/enums/index.ts
new file mode 100644
index 0000000..99fb10b
--- /dev/null
+++ b/src/enums/index.ts
@@ -0,0 +1,20 @@
+export enum MoveCallStatus {
+ BEGIN,
+ FINISH,
+ ERROR,
+};
+
+export enum MoveCallActionType {
+ SET_MNEMONICS = "SET_MNEMONICS",
+ SET_PACKAGE_ID = "SET_PACKAGE_ID",
+ SET_MODULES = "SET_MODULES",
+ SET_ERROR = "SET_ERROR",
+ SET_CURRENT_MODULE = "SET_CURRENT_MODULE",
+ SET_FUNCTIONS = "SET_FUNCTIONS",
+ SET_CURRENT_FUNCTION = "SET_CURRENT_FUNCTION",
+ RESET_ARGS = "RESET_ARGS",
+ RESET_ARGS_USER_INPUT = "RESET_ARGS_USER_INPUT",
+ ADD_ARG = "ADD_ARG",
+ SET_VALUE_TO_ARG = "SET_VALUE_TO_ARG",
+ SET_RESPONSE = "SET_RESPONSE",
+};
\ No newline at end of file
diff --git a/src/extension.ts b/src/extension.ts
index fff25c5..b70fa69 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -4,6 +4,7 @@ import * as vscode from 'vscode';
import { join } from 'path';
import { MessageHandlerData } from '@estruyf/vscode';
import { build, publish, executeCommand } from './suiCommand';
+import { SidebarProvider } from './SidebarProvider';
// This method is called when your extension is activated
// Your extension is activated the very first time the command is executed
@@ -22,6 +23,14 @@ export function activate(context: vscode.ExtensionContext) {
vscode.window.showInformationMessage('Hello World from sui-simulator-vscode!');
});
+ const sidebarProvider = new SidebarProvider(context);
+ context.subscriptions.push(
+ vscode.window.registerWebviewViewProvider(
+ "sui-simulator-sidebar",
+ sidebarProvider
+ )
+ );
+
context.subscriptions.push(disposable);
context.subscriptions.push(vscode.commands.registerCommand("sui-simulator-vscode.webView", () => {
@@ -64,15 +73,15 @@ export function activate(context: vscode.ExtensionContext) {
break;
case "SUI_TERMINAL":
- executeCommand(payload.command);
+ executeCommand(payload.command, payload.suiPath);
break;
case "BUILD":
- build(payload.path);
+ build(payload.packagePath, payload.suiPath);
break;
case "PUBLISH":
- publish(payload.path);
+ publish(payload.packagePath, payload.suiPath);
break;
case "SAVE_ALIASES":
diff --git a/src/suiCommand.ts b/src/suiCommand.ts
index 38f8ae4..973844f 100644
--- a/src/suiCommand.ts
+++ b/src/suiCommand.ts
@@ -1,19 +1,29 @@
import * as vscode from 'vscode';
-export const build = (path: string) => {
+const handleSuiPathEmpty = (suiPath: string) => {
+ if (!suiPath) {
+ return "sui";
+ }
+ return suiPath;
+}
+
+export const build = (path: string, suiPath: string) => {
+ suiPath = handleSuiPathEmpty(suiPath);
const terminal = vscode.window.createTerminal("Sui Simulator");
- terminal.sendText(`sui move build -p ${path}`);
+ terminal.sendText(`${suiPath} move build -p ${path}`);
terminal.show();
};
-export const publish = (path: string) => {
+export const publish = (path: string, suiPath: string) => {
+ suiPath = handleSuiPathEmpty(suiPath);
const terminal = vscode.window.createTerminal("Sui Simulator");
- terminal.sendText(`sui client publish --gas-budget 100000000 ${path}`);
+ terminal.sendText(`${suiPath} client publish --gas-budget 100000000 ${path}`);
terminal.show();
};
-export const executeCommand = (command: string) => {
+export const executeCommand = (command: string, suiPath: string) => {
+ suiPath = handleSuiPathEmpty(suiPath);
const terminal = vscode.window.createTerminal("Sui Simulator");
- terminal.sendText(command);
+ terminal.sendText(`${suiPath} ${command}`);
terminal.show();
};
\ No newline at end of file
diff --git a/src/types/index.ts b/src/types/index.ts
new file mode 100644
index 0000000..42d384d
--- /dev/null
+++ b/src/types/index.ts
@@ -0,0 +1,23 @@
+import { SuiMoveNormalizedFunction } from "@mysten/sui.js/client";
+import { MoveCallStatus } from "../enums";
+
+export interface ActionType {
+ type: string,
+ payload: any
+};
+
+export interface MoveCallState {
+ mnemonics: string,
+ status: MoveCallStatus,
+ packageId: string,
+ modules: string[],
+ currentModule: string,
+ functions: {
+ [key: string]: SuiMoveNormalizedFunction;
+ },
+ currentFunction: string;
+ args: string[],
+ argsUserInput: string[],
+ error: string,
+ response: string,
+}
\ No newline at end of file
diff --git a/src/webview/App.tsx b/src/webview/App.tsx
index a949b2f..cfd3e0d 100644
--- a/src/webview/App.tsx
+++ b/src/webview/App.tsx
@@ -1,13 +1,109 @@
-import React, { useEffect, useState } from "react";
+import React, { useReducer, useState } from "react";
import "./style.css";
import { useSuiClient, useSuiClientContext } from "@mysten/dapp-kit";
-import { DEFAULT_ED25519_DERIVATION_PATH, Ed25519Keypair } from "@mysten/sui.js/keypairs/ed25519";
-import { TransactionArgument, TransactionBlock } from "@mysten/sui.js/transactions";
import { Input } from "./components/Input";
import { Button } from "./components/Button";
import { Aliases } from './components/Aliases';
import { sendMessage } from "./utils/wv_communicate_ext";
-import { getDetailPackage } from "./utils/suiPackage";
+import { ActionType, MoveCallState } from "../types";
+import { MoveCallActionType, MoveCallStatus } from "../enums";
+import { MoveCall } from "./features/moveCall/v2";
+
+const initialState: MoveCallState = {
+ mnemonics: "mouse hood crucial soup report axis awful point stairs guess scrap winter",
+ status: MoveCallStatus.BEGIN,
+ packageId: "",
+ modules: [],
+ currentModule: "",
+ functions: {},
+ currentFunction: "",
+ args: [],
+ argsUserInput: [],
+ error: "",
+ response: "",
+};
+
+const reducer = (state: MoveCallState, action: ActionType): MoveCallState => {
+ const { type, payload } = action;
+ switch (type) {
+ case MoveCallActionType.SET_MNEMONICS:
+ return {
+ ...state,
+ mnemonics: payload,
+ };
+ case MoveCallActionType.SET_PACKAGE_ID:
+ return {
+ ...state,
+ packageId: payload,
+ };
+ case MoveCallActionType.SET_MODULES:
+ return {
+ ...state,
+ modules: payload,
+ };
+
+ case MoveCallActionType.SET_CURRENT_MODULE:
+ return {
+ ...state,
+ currentModule: payload,
+ };
+
+ case MoveCallActionType.SET_FUNCTIONS:
+ return {
+ ...state,
+ functions: payload,
+ };
+
+ case MoveCallActionType.SET_ERROR:
+ return {
+ ...state,
+ status: MoveCallStatus.ERROR,
+ error: payload,
+ };
+
+ case MoveCallActionType.SET_CURRENT_FUNCTION:
+ return {
+ ...state,
+ currentFunction: payload,
+ };
+
+ case MoveCallActionType.RESET_ARGS:
+ return {
+ ...state,
+ args: [],
+ };
+
+ case MoveCallActionType.RESET_ARGS_USER_INPUT:
+ return {
+ ...state,
+ argsUserInput: [],
+ };
+
+ case MoveCallActionType.ADD_ARG:
+ return {
+ ...state,
+ args: [...state.args, payload],
+ };
+
+ case MoveCallActionType.SET_VALUE_TO_ARG:
+ const argsUserInput = [...state.argsUserInput];
+ argsUserInput[payload.index] = payload.value;
+ return {
+ ...state,
+ argsUserInput,
+ };
+
+ case MoveCallActionType.SET_RESPONSE:
+ return {
+ ...state,
+ status: MoveCallStatus.FINISH,
+ response: payload,
+ };
+
+ default:
+ throw new Error(`Unhandled action type: ${type}`);
+ }
+};
export interface IAppProps { }
@@ -15,183 +111,44 @@ export const App: React.FunctionComponent = ({ }: React.PropsWithChil
const suiClient = useSuiClient();
const { network, selectNetwork } = useSuiClientContext();
- const [mnemonics, setMnemonics] = useState("mouse hood crucial soup report axis awful point stairs guess scrap winter");
-
- const [packageId, setPackageId] = useState("0xcab68c8cd7e80f3dd06466da6b2c083d1fd50ab3e9be8e32395c19b53021c064");
- const [module, setModule] = useState("counter");
- const [functionName, setFunctionName] = useState("create");
- const [args, setArgs] = useState([]);
-
- const [error, setError] = useState("");
- const [isError, setIsError] = useState(false);
- const [response, setResponse] = useState("");
+ const [state, dispatch] = useReducer(reducer, initialState);
const [buildPath, setBuildPath] = useState("");
const [publishPath, setPublishPath] = useState("");
-
- let keypair: Ed25519Keypair | null = null;
+ const [suiPath, setSuiPath] = useState("");
const handleNetworkChange = (e: React.ChangeEvent) => {
selectNetwork(e.target.value);
};
- const handleAddArgs = () => {
- setArgs((prevArgs) => [...prevArgs, {
- index: null,
- kind: "Input",
- value: null,
- type: null
- }]);
- };
-
- const handleDeleteArgs = (index: number) => {
- setArgs((prevArgs) => prevArgs.filter((_, i) => i !== index));
- };
-
- const handleMnemonicsChange = (e: React.ChangeEvent) => {
- setMnemonics(e.target.value);
- };
-
- const handleCall = async () => {
- try {
- keypair = Ed25519Keypair.deriveKeypair(
- mnemonics,
- DEFAULT_ED25519_DERIVATION_PATH,
- );
- const privateKey = keypair.getSecretKey();
- const publicKey = keypair.getPublicKey();
- const address = publicKey.toSuiAddress();
-
- const txb = new TransactionBlock();
- txb.setSender(address);
- txb.setGasOwner(address);
- txb.setGasPrice(10000);
-
- // add arguments to the transaction
- for (const arg of args) {
- if (arg.type === "object") {
- txb.object(arg.value);
- } else {
- txb.pure(arg.value);
- }
- }
-
- txb.moveCall({
- arguments: args,
- target: `${packageId}::${module}::${functionName}`
- });
-
- const txBytes = await txb.build({ client: suiClient });
-
- const serializedSignature = (await keypair.signTransactionBlock(txBytes))
- .signature;
-
- const response = await suiClient.executeTransactionBlock({
- transactionBlock: txBytes,
- signature: [serializedSignature],
- options: {
- showEffects: true,
- showObjectChanges: true,
- showBalanceChanges: true,
- showEvents: true,
- showInput: true,
- showRawInput: true,
- },
- });
-
- setIsError(false);
-
- const executionStatus = response.effects?.status;
- if (executionStatus?.status === "failure") {
- setError(executionStatus?.error);
- } else {
- setResponse(JSON.stringify(response.digest));
- }
- return response;
- } catch (err: any) {
- setIsError(true);
- setError(err.message);
- return err;
- }
- };
-
- useEffect(() => {
- getDetailPackage(suiClient, "0xcab68c8cd7e80f3dd06466da6b2c083d1fd50ab3e9be8e32395c19b53021c064").then((data) => {
- const modules = Object.keys(data as {});
-
- if (data) {
- for (const module of modules) {
- const { exposedFunctions } = data[module];
-
- }
- }
- }).catch(err => console.log(err));
- }, []);
-
return (
<>
Sui Simulator
+
+ Setup Sui
+
+ setSuiPath(e.target.value)} />
Network
- Buikd
- setBuildPath(e.target.value)} />
-