diff --git a/README.md b/README.md index 8a0a776..a0c56d4 100644 --- a/README.md +++ b/README.md @@ -28,10 +28,3 @@ Open Sui-simulator extension: You can open by Command Palette if you're using on * Add address and gas: You can create new address by import an existing seed phrase or generate new one * Build, test and Publish: publish your package ID on the Sui network. * Package Explorer: Developers can examine modules, functions, and make calls to them - --- -## Following extension guidelines - -Ensure that you've read through the extensions guidelines and follow the best practices for creating your extension. - -* [Extension Guidelines](https://code.visualstudio.com/api/references/extension-guidelines) diff --git a/media/icon.png b/media/icon.png new file mode 100644 index 0000000..1b68bfb Binary files /dev/null and b/media/icon.png differ diff --git a/media/logo.svg b/media/logo.svg new file mode 100644 index 0000000..1264ad2 --- /dev/null +++ b/media/logo.svg @@ -0,0 +1,3 @@ + + + diff --git a/media/tools.svg b/media/tools.svg deleted file mode 100644 index f1cd568..0000000 --- a/media/tools.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/package.json b/package.json index 34b6376..857da53 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,10 @@ { "name": "sui-simulator-vscode", - "displayName": "sui-simulator-vscode", + "displayName": "sui-simulator", + "icon": "media/icon.png", + "publisher": "weminal-labs", "description": "", - "version": "0.3.0", + "version": "0.4.0", "repository": "https://github.com/Weminal-labs/sui-simulator-vscode", "engines": { "vscode": "^1.87.0" @@ -18,7 +20,7 @@ { "id": "sui-simulator-sidebar-view", "title": "Sui Simulator", - "icon": "media/tools.svg" + "icon": "media/logo.svg" } ] }, @@ -28,7 +30,7 @@ "type": "webview", "id": "sui-simulator-sidebar", "name": "Sui Simulator", - "icon": "media/tools.svg", + "icon": "media/logo.svg", "contextualTitle": "Sui Simulator" } ] diff --git a/src/enums/index.ts b/src/enums/index.ts index 60e1da9..ead662d 100644 --- a/src/enums/index.ts +++ b/src/enums/index.ts @@ -1,5 +1,5 @@ export enum MoveCallStatus { - BEGIN, + NORMAL, FINISH, ERROR, }; @@ -17,6 +17,7 @@ export enum MoveCallActionType { ADD_ARG = "ADD_ARG", SET_VALUE_TO_ARG = "SET_VALUE_TO_ARG", SET_RESPONSE = "SET_RESPONSE", + SET_STATUS_NORMAL = "SET_STATUS_NORMAL", }; export enum SuiCommand { @@ -26,4 +27,8 @@ export enum SuiCommand { SWITCH_NETWORK = "SWITCH_NETWORK", GET_NETWORKS = "GET_NETWORKS", PUBLISH_PACKAGE = "PUBLISH_PACKAGE", + CALL_FUNCTION = "CALL_FUNCTION", + REQUEST_FAUCET = "REQUEST_FAUCET", + BUILD_PACKAGE = "BUILD_PACKAGE", + TEST_PACKAGE = "TEST_PACKAGE", } \ No newline at end of file diff --git a/src/extension.ts b/src/extension.ts index c38979c..a2b4e6a 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -2,7 +2,7 @@ // Import the module and reference it with the alias vscode in your code below import * as vscode from 'vscode'; import { MessageHandlerData } from '@estruyf/vscode'; -import { build, publish } from './suiCommand'; +import { SuiSimulatorTerminal } from './suiSimulatorTerminal'; import { WebviewProvider } from './WebviewProvider'; import { exec } from "child_process"; import { promisify } from "util"; @@ -11,10 +11,10 @@ import { SuiCommand } from './enums'; interface MyCustomTerminalResponse { stdout: string; stderr: { - message: string; - isError: boolean; + message: string; + isError: boolean; }; - } +} // This method is called when your extension is activated // Your extension is activated the very first time the command is executed @@ -73,9 +73,18 @@ export function activate(context: vscode.ExtensionContext) { })); } +let suiSimulatorTerminal = new SuiSimulatorTerminal(); +let suiPath = "sui"; + export const handleReceivedMessage = async (message: any, webView: any, context: any) => { const { command, requestId, payload } = message; + console.log(suiPath); + switch (command) { + case "CHANGE_SUI_PATH": + suiPath = payload.suiPath; + console.log(suiPath); + break; case "SUI_TERMINAL": let resp = { stderr: "", @@ -93,7 +102,7 @@ export const handleReceivedMessage = async (message: any, webView: any, context: switch (payload.cmd) { case SuiCommand.GET_ADDRESSES: - resp = await execNew("sui client addresses --json"); + resp = await execNew(`${suiPath} client addresses --json`); finalResp = { stderr: { @@ -105,7 +114,7 @@ export const handleReceivedMessage = async (message: any, webView: any, context: break; case SuiCommand.GET_GAS_OBJECTS: try { - resp = await execNew("sui client gas --json"); + resp = await execNew(`${suiPath} client gas --json`); finalResp = { stderr: { @@ -120,7 +129,7 @@ export const handleReceivedMessage = async (message: any, webView: any, context: break; case SuiCommand.SWITCH_ADDRESS: - resp = await execNew(`sui client switch --address ${payload.address}`); + resp = await execNew(`${suiPath} client switch --address ${payload.address}`); finalResp = { stderr: { @@ -131,7 +140,7 @@ export const handleReceivedMessage = async (message: any, webView: any, context: }; break; case SuiCommand.GET_NETWORKS: - resp = await execNew("sui client envs --json"); + resp = await execNew(`${suiPath} client envs --json`); finalResp = { stderr: { @@ -142,7 +151,7 @@ export const handleReceivedMessage = async (message: any, webView: any, context: }; break; case SuiCommand.SWITCH_NETWORK: - resp = await execNew(`sui client switch --env ${payload.network}`); + resp = await execNew(`${suiPath} client switch --env ${payload.network}`); finalResp = { stderr: { @@ -154,7 +163,36 @@ export const handleReceivedMessage = async (message: any, webView: any, context: break; case SuiCommand.PUBLISH_PACKAGE: try { - resp = await execNew(`/home/asus/Workspace/sui-testnet-v1.22.0/target/release/sui-ubuntu-x86_64 client publish --gas ${payload.gasObjectId} --gas-budget ${payload.gasBudget} ${vscode.workspace.workspaceFolders?.[0].uri.path} --json --skip-fetch-latest-git-deps --skip-dependency-verification`); + resp = await execNew(`${suiPath} client publish --gas ${payload.gasObjectId} --gas-budget ${payload.gasBudget} ${vscode.workspace.workspaceFolders?.[0].uri.path} --json --skip-fetch-latest-git-deps --skip-dependency-verification`); + + finalResp = { + stderr: { + message: resp.stderr, + isError: false + }, + stdout: resp.stdout + }; + + console.log("stderr:", finalResp.stderr); + console.log("stdout:", finalResp.stdout); + } catch (err: any) { + console.log(err.message); + finalResp = { + stderr: { + message: err.message, + isError: true + }, + stdout: "" + }; + + } + + break; + + case SuiCommand.CALL_FUNCTION: + try { + resp = await execNew(`${suiPath} client call --package ${payload.packageId} --module ${payload.moduleName} --function ${payload.functionName} --json --gas-budget 10000000 ${payload.args.length > 0 ? "--args" : ""} ${payload.args?.join(" ")} + `); finalResp = { stderr: { @@ -179,6 +217,42 @@ export const handleReceivedMessage = async (message: any, webView: any, context: } break; + + case SuiCommand.REQUEST_FAUCET: + try { + resp = await execNew(`${suiPath} client faucet + `); + + finalResp = { + stderr: { + message: resp.stderr, + isError: false + }, + stdout: resp.stdout + }; + + console.log("stderr:", finalResp.stderr); + console.log("stdout:", finalResp.stdout); + } catch (err: any) { + console.log(err.message); + finalResp = { + stderr: { + message: err.message, + isError: true + }, + stdout: "" + }; + + } + break; + + case SuiCommand.BUILD_PACKAGE: + suiSimulatorTerminal.build(vscode.workspace.workspaceFolders?.[0].uri.path, suiPath); + break; + + case SuiCommand.TEST_PACKAGE: + suiSimulatorTerminal.test(vscode.workspace.workspaceFolders?.[0].uri.path, suiPath); + break; } // Do something with the payload @@ -208,14 +282,6 @@ export const handleReceivedMessage = async (message: any, webView: any, context: // 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": context.workspaceState.update(payload.address, { aliases: payload.aliases diff --git a/src/suiCommand.ts b/src/suiCommand.ts deleted file mode 100644 index 973844f..0000000 --- a/src/suiCommand.ts +++ /dev/null @@ -1,29 +0,0 @@ -import * as vscode from 'vscode'; - -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(`${suiPath} move build -p ${path}`); - terminal.show(); -}; - -export const publish = (path: string, suiPath: string) => { - suiPath = handleSuiPathEmpty(suiPath); - const terminal = vscode.window.createTerminal("Sui Simulator"); - terminal.sendText(`${suiPath} client publish --gas-budget 100000000 ${path}`); - terminal.show(); -}; - -export const executeCommand = (command: string, suiPath: string) => { - suiPath = handleSuiPathEmpty(suiPath); - const terminal = vscode.window.createTerminal("Sui Simulator"); - terminal.sendText(`${suiPath} ${command}`); - terminal.show(); -}; \ No newline at end of file diff --git a/src/suiSimulatorTerminal.ts b/src/suiSimulatorTerminal.ts new file mode 100644 index 0000000..e1e1460 --- /dev/null +++ b/src/suiSimulatorTerminal.ts @@ -0,0 +1,35 @@ +import * as vscode from 'vscode'; + +export class SuiSimulatorTerminal { + private terminal: vscode.Terminal; + + constructor() { + this.terminal = vscode.window.createTerminal("Sui Simulator"); + } + + build(packagePath: string | undefined, suiPath: string) { + console.log(this.terminal.exitStatus); + if (this.isCloseTerminal()) { + this.createTerminal(); + } + this.terminal.sendText(`${suiPath} move build -p ${packagePath}`); + this.terminal.show(); + } + + test(packagePath: string | undefined, suiPath: string) { + console.log(this.terminal.exitStatus); + if (this.isCloseTerminal()) { + this.createTerminal(); + } + this.terminal.sendText(`${suiPath} move test -p ${packagePath}`); + this.terminal.show(); + } + + private createTerminal() { + this.terminal = vscode.window.createTerminal("Sui Simulator"); + } + + private isCloseTerminal(): boolean { + return this.terminal.exitStatus instanceof Object; + } +} \ No newline at end of file diff --git a/webview-ui/src/App.tsx b/webview-ui/src/App.tsx index 3003193..c99bbb7 100644 --- a/webview-ui/src/App.tsx +++ b/webview-ui/src/App.tsx @@ -1,62 +1,54 @@ import React from "react"; -import "./style.css"; -import { SuiConfigProvider } from "./context/SuiConfigProvider"; -import { MySuiAccountProvider } from "./context/MySuiAccountProvider"; import { EnvironmentIcon } from "./icons/EnvironmentIcon"; import { Tab } from "./components/Tab"; import { UserIcon } from "./icons/UserIcon"; import { RowVerticalIcon } from "./icons/RowVerticalIcon"; import { ExplorerIcon } from "./icons/ExplorerIcon"; import { Link } from "react-router-dom"; +import { Logo } from "./components/Logo"; -export interface IAppProps {} +export interface IAppProps { } -export const App: React.FunctionComponent = ({}: React.PropsWithChildren) => { +export const App: React.FunctionComponent = ({ }: React.PropsWithChildren) => { return ( <> - - - {/* */} - -
-
-
-
- Sui simulator -
-
- - } - title="Environment" - /> - - {/* */} - - } - title="Gases And Address" - /> - - {/* */} - - } - title="Build, Test And Publish" - /> - - - } - title="Explorer" - /> - -
-
+ {/* */} +
+
+
+
+ +
Sui simulator
+
+
+ + } + title="Environment" + /> + + + } + title="Gases And Address" + /> + + + } + title="Build, Test And Publish" + /> + + + } + title="Explorer" + /> +
- - +
+
); }; diff --git a/webview-ui/src/RootLayout.tsx b/webview-ui/src/RootLayout.tsx index 74df379..0595f2c 100644 --- a/webview-ui/src/RootLayout.tsx +++ b/webview-ui/src/RootLayout.tsx @@ -1,12 +1,14 @@ import React from "react"; import { Outlet } from "react-router-dom"; import { Rainbow } from "./components/Rainbow"; +import { Feedback } from "./components/Feedback"; export const RootLayout = () => { return ( -
+
+
); }; diff --git a/webview-ui/src/components/Feedback.tsx b/webview-ui/src/components/Feedback.tsx new file mode 100644 index 0000000..b2af1ef --- /dev/null +++ b/webview-ui/src/components/Feedback.tsx @@ -0,0 +1,9 @@ +import React from 'react' + +export const Feedback = () => { + return ( + + Feedback + + ) +} diff --git a/webview-ui/src/components/Logo.tsx b/webview-ui/src/components/Logo.tsx new file mode 100644 index 0000000..14abc07 --- /dev/null +++ b/webview-ui/src/components/Logo.tsx @@ -0,0 +1,47 @@ +import React from "react"; + +interface Props { + className?: string; +} + +export const Logo = ({ className }: Props) => { + return ( + + + + + + + + + + + + + + + ); +}; diff --git a/webview-ui/src/components/PackageExplorer.tsx b/webview-ui/src/components/PackageExplorer.tsx new file mode 100644 index 0000000..770cdc6 --- /dev/null +++ b/webview-ui/src/components/PackageExplorer.tsx @@ -0,0 +1,108 @@ +import React from 'react' +import { ArrowLeft } from '../icons/ArrowLeft' +import { Label } from './Label' +import { ArrowDown } from '../icons/ArrowDown' + +export const PackageExplorer = () => { + return ( +
+
+
+
+
+ +
+ Package Explorer +
+
+
+
+
+
+ Input Package +
+
+
+
+ input your package here +
+
+
+
+
+
+ {/*
+
+
+

+ Error: Couldn’t Find Package Id +

+ +
+
+
*/} +
+
+
+

+ Package ID: + 0ghhffghhghf2330056666 +

+
+
+
+ Module +
+ +
+
+
+ Function +
+ +
+
+
+ Args +
+
+
+ U64 +
+
+
+
+ String +
+
+
+
+ Hero +
+
+
+
+
+ Call +
+
+
+
+
+
+
+
+ ) +} diff --git a/webview-ui/src/components/Rainbow.tsx b/webview-ui/src/components/Rainbow.tsx index 6a5f857..d105778 100644 --- a/webview-ui/src/components/Rainbow.tsx +++ b/webview-ui/src/components/Rainbow.tsx @@ -3,7 +3,7 @@ import React from "react"; export const Rainbow = () => { return ( <> -
+
>; + suiPath: string; + setSuiPath: React.Dispatch>; +}; + +const MySuiEnvContext = createContext(null); + +export const MySuiEnvProvider: React.FC<{ children: React.ReactNode }> = ({ + children, +}) => { + const [isSuiFile, setIsSuiFile] = useState(false); + const [suiPath, setSuiPath] = useState(""); + + return ( + + {children} + + ); +}; + +export const useMySuiEnv = () => { + const context = useContext(MySuiEnvContext); + if (context === undefined) { + throw new Error('useMySuiEnv must be used within a MySuiEnvProvider'); + } + return context as MySuiEnvContextType;; +}; diff --git a/webview-ui/src/context/SuiConfigProvider.tsx b/webview-ui/src/context/SuiConfigProvider.tsx deleted file mode 100644 index 236506b..0000000 --- a/webview-ui/src/context/SuiConfigProvider.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import React, { useState } from "react"; -import { createContext, useContext } from "react"; - -export type SuiConfigContextType = { - isSuiCargo: boolean; - setIsSuiCargo: React.Dispatch>; - suiPath: string; - setSuiPath: React.Dispatch>; -}; - -const SuiConfigContext = createContext(null); - -export const SuiConfigProvider: React.FC<{ children: React.ReactNode }> = ({ - children, -}) => { - const [isSuiCargo, setIsSuiCargo] = useState(false); - const [suiPath, setSuiPath] = useState(""); - - return ( - - {children} - - ); -}; - -export const useSuiConfig = () => { - const context = useContext(SuiConfigContext); - if (context === undefined) { - throw new Error('useSuiConfig must be used within a SuiConfigProvider'); - } - return context as SuiConfigContextType;; -}; diff --git a/webview-ui/src/features/buildTestPublish/v2/index.tsx b/webview-ui/src/features/buildTestPublish/index.tsx similarity index 88% rename from webview-ui/src/features/buildTestPublish/v2/index.tsx rename to webview-ui/src/features/buildTestPublish/index.tsx index 4058f1c..a832896 100644 --- a/webview-ui/src/features/buildTestPublish/v2/index.tsx +++ b/webview-ui/src/features/buildTestPublish/index.tsx @@ -1,12 +1,12 @@ import React, { useState } from "react"; -import { useMySuiAccount } from "../../../context/MySuiAccountProvider"; -import { requestDataFromTerminal } from "../../../utils/wv_communicate_ext"; -import { SuiCommand } from "../../../../../src/enums"; +import { useMySuiAccount } from "../../context/MySuiAccountProvider"; +import { requestDataFromTerminal } from "../../utils/wv_communicate_ext"; +import { SuiCommand } from "../../../../src/enums"; import { useNavigate } from "react-router-dom"; -import { Label } from "../../../components/Label"; -import { ArrowLeft } from "../../../icons/ArrowLeft"; -import { shortenAddress, shortenObjectType } from "../../../utils/address_shortener"; -import { Error } from "../../../components/Error"; +import { Label } from "../../components/Label"; +import { ArrowLeft } from "../../icons/ArrowLeft"; +import { shortenAddress, shortenObjectType } from "../../utils/address_shortener"; +import { Error } from "../../components/Error"; export const BuildTestPublish = () => { const { currentAddress, currentGasObject } = useMySuiAccount(); @@ -24,6 +24,18 @@ export const BuildTestPublish = () => { navigate("/"); }; + const handleTest = async () => { + await requestDataFromTerminal({ + cmd: SuiCommand.TEST_PACKAGE, + }); + } + + const handleBuild = async () => { + await requestDataFromTerminal({ + cmd: SuiCommand.BUILD_PACKAGE, + }); + } + const handlePublish = async () => { setIsLoading(true); const resp = await requestDataFromTerminal({ @@ -114,8 +126,8 @@ export const BuildTestPublish = () => { return ( <> -
-
+
+
{
- -
-
-
-
- Output -
-
-
-
-
- Output... -
-
-
-
-
- Result -
{isLoading ? ( "Publishing...." ) : ( @@ -210,10 +205,12 @@ export const BuildTestPublish = () => { {isError && } {!isError && ( <> -
Transaction: {currentDigest}
- -

Effects:

- {/* Package id: {packageId} */} + {currentDigest && <> +
+ Result +
+
Transaction: {currentDigest}
+ }
{uniquePackages.map((pkg) => { diff --git a/webview-ui/src/features/buildTestPublish/v1/index.tsx b/webview-ui/src/features/buildTestPublish/v1/index.tsx deleted file mode 100644 index 34135e0..0000000 --- a/webview-ui/src/features/buildTestPublish/v1/index.tsx +++ /dev/null @@ -1,10 +0,0 @@ -{/*

Build

- setBuildPath(e.target.value)} /> - -

Publish

- setPublishPath(e.target.value)} /> - */} \ No newline at end of file diff --git a/webview-ui/src/features/gasAddress/gas.tsx b/webview-ui/src/features/gasAddress/gas.tsx index 931ad71..4db2e7f 100644 --- a/webview-ui/src/features/gasAddress/gas.tsx +++ b/webview-ui/src/features/gasAddress/gas.tsx @@ -1,7 +1,7 @@ import React, { useEffect, useState } from "react"; import { requestDataFromTerminal } from "../../utils/wv_communicate_ext"; import { SuiCommand } from "../../../../src/enums"; -import { useSuiClientContext } from "@mysten/dapp-kit"; +import { useSuiClient, useSuiClientContext } from "@mysten/dapp-kit"; import { useMySuiAccount } from "../../context/MySuiAccountProvider"; import { shortenAddress } from "../../utils/address_shortener"; @@ -13,12 +13,34 @@ export interface GasObject { export const Gas = () => { // remember that then change UI in here need to call to terminal const { network } = useSuiClientContext(); + const suiClient = useSuiClient(); const { currentAddress, currentGasObject, gasObjects, setCurrentGasObject, setGasObjects } = useMySuiAccount(); const [isLoading, setIsLoading] = useState(false); + const [reload, setReload] = useState(1); - const requestFaucet = () => {}; + const requestFaucet = async () => { + const resp = await requestDataFromTerminal({ + cmd: SuiCommand.REQUEST_FAUCET, + }); + const { stdout, stderr } = resp; + setIsLoading(true); + setTimeout(() => { + setReload(Math.random()) + setIsLoading(false); + }, 10000) + }; + + // useEffect(() => { + // async function getGasObjects() { + // const resp = await suiClient.getAllCoins({ owner: currentAddress }) + // console.log(resp); + // } + // if (currentAddress) { + // getGasObjects(); + // } + // }, [currentAddress]) useEffect(() => { async function getGasObjects() { @@ -33,7 +55,7 @@ export const Gas = () => { // console.log(objects); } getGasObjects(); - }, [network, currentAddress]); + }, [network, currentAddress, reload]); const balanceOfCurrentGasObject = gasObjects.find( (gasObject) => gasObject.gasCoinId === currentGasObject @@ -95,7 +117,7 @@ export const Gas = () => {
)} - - ; - } - - )} - - - {!isError ?

Result: {response}

:

Error: {error}

} - - ); -}; diff --git a/webview-ui/src/features/moveCall/v2/MoveCall.tsx b/webview-ui/src/features/moveCall/v2/MoveCall.tsx deleted file mode 100644 index 2a7de02..0000000 --- a/webview-ui/src/features/moveCall/v2/MoveCall.tsx +++ /dev/null @@ -1,250 +0,0 @@ -import React, { useEffect } from "react"; -import { ActionType, MoveCallState } from "../../../types"; -import { Input } from "../../../components/Input"; -import { MoveCallActionType, MoveCallStatus } from "../../../../../src/enums"; -import { useSuiClient } from "@mysten/dapp-kit"; -import { Button } from "../../../components/Button"; -import { DEFAULT_ED25519_DERIVATION_PATH, Ed25519Keypair } from "@mysten/sui.js/keypairs/ed25519"; -import { TransactionBlock } from "@mysten/sui.js/transactions"; -import { useNavigate } from "react-router-dom"; - -export interface IMoveCallProps { - state: MoveCallState; - dispatch: React.Dispatch; -} - -export const MoveCall = ({ state, dispatch }: IMoveCallProps) => { - const suiClient = useSuiClient(); - - const { - mnemonics, - packageId, - args, - argsUserInput, - currentFunction, - currentModule, - error, - functions, - modules, - response, - status, - } = state; - - const handleMnemonicsChange = (e: React.ChangeEvent) => { - dispatch({ type: MoveCallActionType.SET_MNEMONICS, payload: e.target.value }); - }; - - const handlePackageIdChange = (e: React.ChangeEvent) => { - dispatch({ type: MoveCallActionType.SET_PACKAGE_ID, payload: e.target.value }); - }; - - const handleChooseModule = async (e: React.ChangeEvent) => { - if (e.target.value) { - dispatch({ type: MoveCallActionType.SET_CURRENT_MODULE, payload: e.target.value }); - - try { - const module = await suiClient.getNormalizedMoveModule({ - package: packageId, - module: e.target.value, - }); - const { exposedFunctions } = module; - dispatch({ type: MoveCallActionType.SET_FUNCTIONS, payload: exposedFunctions }); - } catch (err: any) { - dispatch({ type: MoveCallActionType.SET_ERROR, payload: err.message }); - } - } - }; - - const handleChooseFunction = async (e: React.ChangeEvent) => { - if (e.target.value) { - dispatch({ type: MoveCallActionType.SET_CURRENT_FUNCTION, payload: e.target.value }); - dispatch({ type: MoveCallActionType.RESET_ARGS }); - dispatch({ type: MoveCallActionType.RESET_ARGS_USER_INPUT }); - - const fn = functions[e.target.value]; - const { parameters } = fn; - for (const param of parameters) { - // handle typescript error by this way is suck => refactor later - if (typeof param === "object") { - if ("MutableReference" in param) { - if (typeof param.MutableReference === "object" && "Struct" in param.MutableReference) { - let { - Struct: { address, module, name }, - } = param.MutableReference; - if (name !== "TxContext") { - dispatch({ - type: MoveCallActionType.ADD_ARG, - payload: `${address}::${module}::${name}`, - }); - } - } - } else if ("Reference" in param) { - } else if ("Vector" in param) { - } else if ("Struct" in param) { - } else if ("TypeParameter" in param) { - } - } else { - dispatch({ type: MoveCallActionType.ADD_ARG, payload: param }); - } - } - } - }; - - const handleSetValueToArg = (index: number, value: string) => { - dispatch({ - type: MoveCallActionType.SET_VALUE_TO_ARG, - payload: { index, value }, - }); - }; - - useEffect(() => { - async function getModules() { - try { - const modules = await suiClient.getNormalizedMoveModulesByPackage({ package: packageId }); - dispatch({ type: MoveCallActionType.SET_MODULES, payload: modules }); - } catch (err: any) { - dispatch({ type: MoveCallActionType.SET_ERROR, payload: err.message }); - } - } - - if (packageId) { - getModules(); - } - }, [packageId]); - - const modulesName = Object.keys(modules as {}); - const functionsName = Object.keys(functions as {}); - - let keypair: Ed25519Keypair | null = null; - - 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); - - const argsFinal = argsUserInput.map((ele) => { - if (ele.startsWith("0x")) { - return txb.object(ele); - } else { - return txb.pure(ele); - } - }); - - txb.moveCall({ - arguments: argsFinal, - target: `${packageId}::${currentModule}::${currentFunction}`, - }); - - 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, - }, - }); - - const executionStatus = response.effects?.status; - if (executionStatus?.status === "failure") { - dispatch({ type: MoveCallActionType.SET_ERROR, payload: executionStatus?.error }); - } else { - dispatch({ - type: MoveCallActionType.SET_RESPONSE, - payload: JSON.stringify(response.digest), - }); - } - return response; - } catch (err: any) { - dispatch({ type: MoveCallActionType.SET_ERROR, payload: err.message }); - return err; - } - }; - - const navigate = useNavigate(); - - const handleNavigate = () => { - navigate("/"); - }; - - return ( - <> -

Still Developing

-

Back

-

Call

-
- Mnemonics: - -
-
- Package: - -
-
- {modulesName.length > 0 && ( - <> - Module: - - - )} -
-
- {currentModule.length > 0 && ( - <> - Function: - - - )} -
- {currentFunction.length > 0 && ( - <> -

Args

- {args.map((arg, index) => { - return ( -
- handleSetValueToArg(index, e.target.value)} - /> - {/* value need to set like above if not will have bug when choose between functions */} -
- ); - })} - - )} - - - {status === MoveCallStatus.FINISH &&

Result: ${response}

} - {status === MoveCallStatus.ERROR &&

Error: ${error}

} - - ); -}; diff --git a/webview-ui/src/features/packageExplorer/MoveCall.tsx b/webview-ui/src/features/packageExplorer/MoveCall.tsx new file mode 100644 index 0000000..4e81db4 --- /dev/null +++ b/webview-ui/src/features/packageExplorer/MoveCall.tsx @@ -0,0 +1,411 @@ +import React, { useEffect, useState } from "react"; +import { MoveCallState } from "../../types"; +import { MoveCallActionType, MoveCallStatus, SuiCommand } from "../../../../src/enums"; +import { useSuiClient, useSuiClientContext } from "@mysten/dapp-kit"; +import { useNavigate } from "react-router-dom"; +import { Label } from "../../components/Label"; +import { ArrowLeft } from "../../icons/ArrowLeft"; +import { shortenAddress, shortenObjectType } from "../../utils/address_shortener"; +import { requestDataFromTerminal } from "../../utils/wv_communicate_ext"; +import { Error } from "../../components/Error"; + +export interface IMoveCallProps { + state: MoveCallState; + dispatch: React.Dispatch; +} + +export const MoveCall = ({ state, dispatch }: IMoveCallProps) => { + const suiClient = useSuiClient(); + const { network, selectNetwork } = useSuiClientContext(); + + const { + packageId, + args, + argsUserInput, + currentFunction, + currentModule, + error, + functions, + modules, + response, + status, + } = state; + + const [isPackageIdValid, setIsPackageIdValid] = React.useState(false); + const [objects, setObjects] = useState([]); // set type later + + const handlePackageIdChange = (e: React.ChangeEvent) => { + dispatch({ type: MoveCallActionType.SET_PACKAGE_ID, payload: e.target.value }); + }; + + const handleChooseModule = async (e: React.ChangeEvent) => { + if (e.target.value) { + dispatch({ type: MoveCallActionType.SET_CURRENT_MODULE, payload: e.target.value }); + + try { + const module = await suiClient.getNormalizedMoveModule({ + package: packageId, + module: e.target.value, + }); + const { exposedFunctions } = module; + dispatch({ type: MoveCallActionType.SET_FUNCTIONS, payload: exposedFunctions }); + } catch (err: any) { + dispatch({ type: MoveCallActionType.SET_ERROR, payload: err.message }); + } + } + }; + + const handleChooseFunction = async (e: React.ChangeEvent) => { + if (e.target.value) { + dispatch({ type: MoveCallActionType.SET_CURRENT_FUNCTION, payload: e.target.value }); + dispatch({ type: MoveCallActionType.RESET_ARGS }); + dispatch({ type: MoveCallActionType.RESET_ARGS_USER_INPUT }); + + const fn = functions[e.target.value]; + const { parameters } = fn; + for (const param of parameters) { + // handle typescript error by this way is suck => refactor later + if (typeof param === "object") { + if ("MutableReference" in param) { + if (typeof param.MutableReference === "object" && "Struct" in param.MutableReference) { + let { + Struct: { address, module, name }, + } = param.MutableReference; + if (name !== "TxContext") { + dispatch({ + type: MoveCallActionType.ADD_ARG, + payload: `${address}::${module}::${name}`, + }); + } + } + } else if ("Reference" in param) { + } else if ("Vector" in param) { + } else if ("Struct" in param) { + } else if ("TypeParameter" in param) { + } + } else { + dispatch({ type: MoveCallActionType.ADD_ARG, payload: param }); + } + } + } + }; + + const handleSetValueToArg = (index: number, value: string) => { + dispatch({ + type: MoveCallActionType.SET_VALUE_TO_ARG, + payload: { index, value }, + }); + }; + + useEffect(() => { + async function getModules() { + try { + const modules = await suiClient.getNormalizedMoveModulesByPackage({ package: packageId }); + setIsPackageIdValid(true); + dispatch({ type: MoveCallActionType.SET_STATUS_NORMAL }); + dispatch({ type: MoveCallActionType.SET_MODULES, payload: modules }); + } catch (err: any) { + dispatch({ type: MoveCallActionType.SET_ERROR, payload: err.message }); + setIsPackageIdValid(false); + } + } + + if (packageId) { + getModules(); + } + }, [packageId]); + + const modulesName = Object.keys(modules as {}); + const functionsName = Object.keys(functions as {}); + + const handleCall = async () => { + try { + const resp = await requestDataFromTerminal({ + cmd: SuiCommand.CALL_FUNCTION, + packageId, + moduleName: currentModule, + functionName: currentFunction, + args: argsUserInput, + }); + const { stdout, stderr } = resp; + // const objects = JSON.parse(stdout); + + if (stderr.isError) { + dispatch({ type: MoveCallActionType.SET_ERROR, payload: stderr.message }); + } else { + const { digest, objectChanges } = JSON.parse(stdout); + setObjects(objectChanges); + console.log(digest); + console.log(objectChanges); + } + console.log(stdout); + console.log(stderr); + dispatch({ type: MoveCallActionType.SET_RESPONSE, payload: stdout }); + } catch (err: any) { + dispatch({ type: MoveCallActionType.SET_ERROR, payload: err.message }); + } + } + + const navigate = useNavigate(); + + const handleNavigate = () => { + navigate("/"); + }; + + const packages = objects + .map((obj) => { + if (obj.type !== "published") { + const packageName = obj.objectType.split("::")[0]; + return packageName; + } + }) + .filter((item) => item !== undefined); + + let uniquePackages = [...new Set(packages)]; + + uniquePackages = uniquePackages.map((pkg) => { + return { + packageName: pkg, + modules: [], + }; + }); + + const getModulesOfPackage = (packageName: string) => { + console.log(packageName); + const modules = objects + .map((obj) => { + const { objectType, type } = obj; + if (type !== "published") { + console.log(objectType); + if (objectType.startsWith(packageName)) { + console.log(objectType.split("::")); + return objectType.split("::")[1]; + } + } + }) + .filter((item) => item !== undefined); + const uniqueModules = [...new Set(modules)]; + console.log(uniqueModules); + return uniqueModules; + }; + + const getObjectsOfModule = (packageName: string, moduleName: string) => { + const objectOfModule = objects + .map((obj) => { + if (obj.type !== "published") { + if ( + obj.objectType.startsWith(packageName) && + obj.objectType.split("::")[1] === moduleName + ) { + return obj; + } + } + }) + .filter((item) => item !== undefined); + return objectOfModule; + }; + + return ( + <> +
+
+
+
+
+ +
+ Package Explorer +
+
+
+
+
+
+ Input Package +
+
+ +
+
+
+
+ {/*
+
+
+

+ Error: Couldn’t Find Package Id +

+ +
+
+
*/} + {isPackageIdValid &&
+
+
+

+ Package ID: + {shortenAddress(packageId, 5)} +

+
+ {/*
+
+ Module +
+ +
*/} + + {modulesName.length > 0 && ( + <> + + + )} + + {/*
+
+ Function +
+ +
*/} + + {currentModule.length > 0 && ( + <> + + + )} + + {args.length > 0 &&
+
+ Args +
+ {currentFunction.length > 0 && ( + <> + {args.map((arg, index) => { + return ( +
+ handleSetValueToArg(index, e.target.value)} /> + {/* value need to set like above if not will have bug when choose between functions */} +
+ ); + })} + + )} + {/*
+
+ U64 +
+
+
+
+ String +
+
+
+
+ Hero +
+
*/} +
} + + +
+
} + + {status === MoveCallStatus.FINISH && <> +
+ Result +
+ +
+ {uniquePackages.map((pkg) => { + return ( +
+
+
+

+ Package ID: + + {shortenAddress(pkg.packageName, 5)} + +

+
+ {getModulesOfPackage(pkg.packageName).map((module) => { + return ( +
+
Module: {module}
+ {getObjectsOfModule(pkg.packageName, module).map((obj) => { + return ( +
+
Object id:{shortenAddress(obj.objectId, 5)}
+
+ Object type: {shortenObjectType(obj.objectType, 5)} +
+
+ ); + })} +
+ ); + })} +
+
+ ); + })} +
+ + {/* {objects.map((obj) => { + if (obj.type !== "published") { + return ( + <> +
+
Object id: {obj.objectId}
+
Object type: {obj.objectType}
+
+ + ); + } + })} */} + } + {status === MoveCallStatus.ERROR && } +
+
+
+
+ + ); +}; diff --git a/webview-ui/src/features/moveCall/v2/index.tsx b/webview-ui/src/features/packageExplorer/index.tsx similarity index 87% rename from webview-ui/src/features/moveCall/v2/index.tsx rename to webview-ui/src/features/packageExplorer/index.tsx index 1fef5ed..9e20783 100644 --- a/webview-ui/src/features/moveCall/v2/index.tsx +++ b/webview-ui/src/features/packageExplorer/index.tsx @@ -1,11 +1,10 @@ import React, { useReducer } from "react"; import { MoveCall } from "./MoveCall"; -import { MoveCallActionType, MoveCallStatus } from "../../../../../src/enums"; -import { ActionType, MoveCallState } from "../../../types"; +import { MoveCallActionType, MoveCallStatus } from "../../../../src/enums"; +import { ActionType, MoveCallState } from "../../types"; const initialState: MoveCallState = { - mnemonics: "mouse hood crucial soup report axis awful point stairs guess scrap winter", - status: MoveCallStatus.BEGIN, + status: MoveCallStatus.NORMAL, packageId: "", modules: [], currentModule: "", @@ -20,10 +19,10 @@ const initialState: MoveCallState = { const reducer = (state: MoveCallState, action: ActionType): MoveCallState => { const { type, payload } = action; switch (type) { - case MoveCallActionType.SET_MNEMONICS: + case MoveCallActionType.SET_STATUS_NORMAL: return { ...state, - mnemonics: payload, + status: MoveCallStatus.NORMAL, }; case MoveCallActionType.SET_PACKAGE_ID: return { @@ -99,7 +98,8 @@ const reducer = (state: MoveCallState, action: ActionType): MoveCallState => { } }; -export default function index() { +export const PackageExplorer = () => { const [state, dispatch] = useReducer(reducer, initialState); return ; } + diff --git a/webview-ui/src/features/suiConfig/v1/index.tsx b/webview-ui/src/features/suiConfig/v1/index.tsx deleted file mode 100644 index 6e1c00c..0000000 --- a/webview-ui/src/features/suiConfig/v1/index.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import React, { useState } from "react"; -import { Input } from "../../../components/Input"; -import { useSuiClientContext } from "@mysten/dapp-kit"; - -export default function index() { - const { network, selectNetwork } = useSuiClientContext(); - const [suiPath, setSuiPath] = useState(""); - - const handleNetworkChange = (e: React.ChangeEvent) => { - selectNetwork(e.target.value); - }; - - return ( - <> -

Setup Sui

- setSuiPath(e.target.value)} - /> -

Network

- - - ); -} diff --git a/webview-ui/src/features/suiConfig/v2/index.tsx b/webview-ui/src/features/suiEnv/index.tsx similarity index 80% rename from webview-ui/src/features/suiConfig/v2/index.tsx rename to webview-ui/src/features/suiEnv/index.tsx index d48046e..5e5d68b 100644 --- a/webview-ui/src/features/suiConfig/v2/index.tsx +++ b/webview-ui/src/features/suiEnv/index.tsx @@ -1,18 +1,19 @@ import React, { useEffect, useRef, useState } from "react"; import Toggle from "react-toggle"; import "react-toggle/style.css"; -import { convertWindowsToUnixPath } from "../../../utils"; -import { FileWithPath } from "../../../types"; +import { convertWindowsToUnixPath } from "../../utils"; +import { FileWithPath } from "../../types"; import { useSuiClientContext } from "@mysten/dapp-kit"; -import { useSuiConfig } from "../../../context/SuiConfigProvider"; -import { requestDataFromTerminal } from "../../../utils/wv_communicate_ext"; -import { SuiCommand } from "../../../../../src/enums"; -import { ArrowLeft } from "../../../icons/ArrowLeft"; +import { useMySuiEnv } from "../../context/MySuiEnvProvider"; +import { requestDataFromTerminal } from "../../utils/wv_communicate_ext"; +import { SuiCommand } from "../../../../src/enums"; +import { ArrowLeft } from "../../icons/ArrowLeft"; import { useNavigate } from "react-router-dom"; +import { messageHandler } from "@estruyf/vscode/dist/client"; -export const SuiConfig = () => { +export const SuiEnv = () => { const { network, selectNetwork } = useSuiClientContext(); - const { isSuiCargo, setIsSuiCargo, suiPath, setSuiPath } = useSuiConfig(); + const { isSuiFile, setIsSuiFile, suiPath, setSuiPath } = useMySuiEnv(); const [userNetworks, setUserNetworks] = useState([]); // type later const [isLoading, setIsLoading] = useState(false); @@ -32,6 +33,20 @@ export const SuiConfig = () => { setIsLoading(false); }; + const handleNavigate = () => { + navigate("/"); + }; + + const handleToogle = (e: React.ChangeEvent) => { + setIsSuiFile(e.target.checked) + } + + useEffect(() => { + if (!isSuiFile) { + messageHandler.send("CHANGE_SUI_PATH", { suiPath: "sui" }); + } + }, [isSuiFile]) + useEffect(() => { async function getUserNetworks() { setIsLoading(true); @@ -50,10 +65,6 @@ export const SuiConfig = () => { getUserNetworks(); }, []); - const handleNavigate = () => { - navigate("/"); - }; - useEffect(() => { // fileInputRef?.current?.setAttribute("directory", ""); // fileInputRef?.current?.setAttribute("webkitdirectory", ""); @@ -66,12 +77,13 @@ export const SuiConfig = () => { console.log( convertWindowsToUnixPath((fileInputRef.current?.files?.item(0) as FileWithPath)?.path) ); + messageHandler.send("CHANGE_SUI_PATH", { suiPath: convertWindowsToUnixPath((fileInputRef.current?.files?.item(0) as FileWithPath)?.path) }); }); }, [fileInputRef]); return ( <> -
+
@@ -88,26 +100,19 @@ export const SuiConfig = () => {
- Sui Cargo + Binaries
setIsSuiCargo(e.target.checked)} + onChange={handleToogle} />
- {!isSuiCargo && ( - <> -
-
- -
-
-
{suiPath &&

{suiPath}

}
- - )} + {/* not sure why bug if use conditional rendering? when this input element is not rendered for first time, the ref to this element will always be null */} + +
{isSuiFile && suiPath &&

{suiPath}

}
{isLoading ? ( "Loading" ) : ( diff --git a/webview-ui/src/index.tsx b/webview-ui/src/index.tsx index 0be6105..bf3b602 100644 --- a/webview-ui/src/index.tsx +++ b/webview-ui/src/index.tsx @@ -6,12 +6,12 @@ import { Route, MemoryRouter, Routes } from "react-router-dom"; import { SuiClientProvider } from "@mysten/dapp-kit"; import { networkConfig } from "./configs/networkConfig"; import { RootLayout } from "./RootLayout"; -import { SuiConfig } from "./features/suiConfig/v2"; +import { SuiEnv } from "./features/suiEnv"; import { GasAddress } from "./features/gasAddress/"; -import { BuildTestPublish } from "./features/buildTestPublish/v2"; -import { SuiConfigProvider } from "./context/SuiConfigProvider"; +import { BuildTestPublish } from "./features/buildTestPublish"; +import { MySuiEnvProvider } from "./context/MySuiEnvProvider"; import { MySuiAccountProvider } from "./context/MySuiAccountProvider"; -import Explorer from "./features/moveCall/v2/index"; +import { PackageExplorer } from "./features/packageExplorer/index"; declare const acquireVsCodeApi: () => { getState: () => T; @@ -26,21 +26,21 @@ if (root) { root.render( - + }> } /> - } /> + } /> } /> } /> - } /> + } /> - + ); diff --git a/webview-ui/src/style.css b/webview-ui/src/style.css deleted file mode 100644 index 3944a4c..0000000 --- a/webview-ui/src/style.css +++ /dev/null @@ -1,3 +0,0 @@ -h1 { - font-family: var(--vscode-editor-font-family); -} \ No newline at end of file diff --git a/webview-ui/src/types/index.ts b/webview-ui/src/types/index.ts index 8dbdf29..bfbd594 100644 --- a/webview-ui/src/types/index.ts +++ b/webview-ui/src/types/index.ts @@ -7,7 +7,6 @@ export interface ActionType { } export interface MoveCallState { - mnemonics: string; status: MoveCallStatus; packageId: string; modules: string[];