diff --git a/packages/demo/package.json b/packages/demo/package.json index ed50074f..742a2cdb 100644 --- a/packages/demo/package.json +++ b/packages/demo/package.json @@ -16,14 +16,18 @@ }, "dependencies": { "@lit/react": "^1.0.5", + "@uiw/react-json-view": "2.0.0-alpha.30", "lucide-react": "^0.427.0", "next": "14.2.10", "react": "^18", - "react-dom": "^18" + "react-dom": "^18", + "reflect-metadata": "^0.2.0" }, "devDependencies": { "@ckb-ccc/connector-react": "workspace:*", "@ckb-ccc/lumos-patches": "workspace:*", + "@ckb-ccc/ssri": "workspace:*", + "@ckb-ccc/udt": "workspace:*", "@ckb-lumos/ckb-indexer": "^0.24.0-next.1", "@ckb-lumos/common-scripts": "^0.24.0-next.1", "@ckb-lumos/config-manager": "^0.24.0-next.1", diff --git a/packages/demo/src/app/connected/(tools)/SSRI/page.tsx b/packages/demo/src/app/connected/(tools)/SSRI/page.tsx new file mode 100644 index 00000000..62433095 --- /dev/null +++ b/packages/demo/src/app/connected/(tools)/SSRI/page.tsx @@ -0,0 +1,971 @@ +"use client"; + +import "reflect-metadata"; +import React, { useState, useEffect, useCallback } from "react"; +import { Button } from "@/src/components/Button"; +import { TextInput } from "@/src/components/Input"; +import { useApp } from "@/src/context"; +import { ButtonsPanel } from "@/src/components/ButtonsPanel"; +import { Dropdown } from "@/src/components/Dropdown"; +import { + ScriptAmountArrayInput, + ScriptAmountType, +} from "@/src/components/ScriptAmountInput"; +import { ssri } from "@ckb-ccc/ssri"; +import { ccc } from "@ckb-ccc/connector-react"; +import JsonView from "@uiw/react-json-view"; +import { darkTheme } from "@uiw/react-json-view/dark"; +import Image from "next/image"; +import { HexArrayInput } from "@/src/components/HexArrayInput"; +import { Icon } from "@/src/components/Icon"; + +type ParamValue = + | string + | ScriptAmountType[] + | ccc.ScriptLike + | ccc.CellLike + | ccc.TransactionLike + | boolean + | undefined + | ccc.HexLike[]; + +type MethodParamType = + | "contextScript" + | "contextCell" + | "contextTransaction" + | "scriptAmountArray" + | "scriptArray" + | "tx" + | "signer" + | "hexArray" + | "hex" + | "stringArray" + | "number"; + +interface MethodParam { + name: string; + type?: MethodParamType; +} + +const METHODS_OPTIONS = [ + "SSRI.version", + "SSRI.getMethods", + "SSRI.hasMethods", + "Customized", +]; + +type IconName = "Hash" | "Code"; + +const PARAM_TYPE_OPTIONS: { + name: string; + displayName: string; + iconName: IconName; +}[] = [ + { name: "contextScript", displayName: "Context Script", iconName: "Code" }, + { name: "contextCell", displayName: "Context Cell", iconName: "Code" }, + { + name: "contextTransaction", + displayName: "Context Transaction", + iconName: "Code", + }, + { + name: "scriptAmountArray", + displayName: "Script Amount Array", + iconName: "Code", + }, + { name: "scriptArray", displayName: "Script Array", iconName: "Code" }, + { name: "tx", displayName: "Transaction", iconName: "Code" }, + { name: "signer", displayName: "Signer", iconName: "Code" }, + // { name: "hexArray", displayName: "Hex Array", iconName: "Code" }, + { name: "hex", displayName: "Generic Data (HexLike)", iconName: "Code" }, + { + name: "stringArray", + displayName: "String Array (comma-separated)", + iconName: "Code", + }, +]; + +export default function SSRI() { + const { signer, createSender } = useApp(); + const { log, error } = createSender("SSRI"); + + const [SSRIExecutorURL, setSSRIExecutorURL] = useState( + "http://localhost:9090", + ); + const [contractOutPointTx, setContractOutPointTx] = useState(""); + const [contractOutPointIndex, setContractOutPointIndex] = + useState("0"); + const [methodParams, setMethodParams] = useState([]); + const [paramValues, setParamValues] = useState>( + {}, + ); + const [methodResult, setMethodResult] = useState(undefined); + const [SSRICallDetails, setSSRICallDetails] = useState(null); + const [iconDataURL, setIconDataURL] = useState(""); + const [ssriContractTypeIDArgs, setSsriContractTypeIDArgs] = useState( + "0x8fd55df879dc6176c95f3c420631f990ada2d4ece978c9512c39616dead2ed56", + ); + const [showSSRICallDetails, setShowSSRICallDetails] = + useState(false); + const [isLoading, setIsLoading] = useState(false); + const [rawMethodPath, setRawMethodPath] = useState("SSRI.version"); + const [isBuiltIn, setIsBuiltIn] = useState(true); + const [selectedParamType, setSelectedParamType] = + useState("contextScript"); + const [methodPathInput, setMethodPathInput] = useState(""); + + const addMethodParam = () => { + const contextTypes = ["contextScript", "contextCell", "contextTransaction"]; + const hasContextParam = methodParams.some( + (param) => param.type && contextTypes.includes(param.type), + ); + + if (contextTypes.includes(selectedParamType) && hasContextParam) { + error( + "Invalid Parameter: You can only have one context parameter (Script, Cell, or Transaction)", + ); + return; + } + + setMethodParams([ + ...methodParams, + { + name: `Parameter${methodParams.length}`, + type: selectedParamType, + }, + ]); + }; + + const deleteMethodParam = (index: number) => { + setMethodParams(methodParams.filter((_, i) => i !== index)); + }; + + const getOutPointFromTypeIDArgs = useCallback(async () => { + if (!signer) return; + const scriptCell = await signer.client.findSingletonCellByType({ + codeHash: + "0x00000000000000000000000000000000000000000000000000545950455f4944", + hashType: "type", + args: ssriContractTypeIDArgs, + }); + if (!scriptCell) { + throw new Error("PUDT script cell not found"); + } + const targetOutPoint = scriptCell.outPoint; + setContractOutPointTx(targetOutPoint.txHash); + setContractOutPointIndex(targetOutPoint.index.toString()); + }, [signer, ssriContractTypeIDArgs]); + + useEffect(() => { + getOutPointFromTypeIDArgs(); + }, [ssriContractTypeIDArgs, signer, getOutPointFromTypeIDArgs]); + + const makeSSRICall = async () => { + if (!signer) return; + + setIsLoading(true); + setMethodResult(undefined); + setIconDataURL(""); + + const testSSRIExecutor = new ssri.ExecutorJsonRpc(SSRIExecutorURL); + + let contract: ssri.Trait | undefined; + try { + const targetOutPoint = { + txHash: contractOutPointTx, + index: parseInt(contractOutPointIndex), + }; + const scriptCell = await signer.client.getCell(targetOutPoint); + + if (!scriptCell) { + throw new Error("Script cell not found"); + } + + if (!scriptCell.cellOutput.type?.hash()) { + throw new Error("Script cell type hash not found"); + } + contract = new ssri.Trait(scriptCell.outPoint, testSSRIExecutor); + + // Check contract is defined before using + if (!contract) { + throw new Error("Contract not initialized"); + } + + // Initialize context object + let context = {}; + + // Prepare arguments, separating context params from regular args + const args = methodParams + .filter( + (paramType) => + !["contextScript", "contextCell", "contextTransaction"].includes( + paramType.type || "", + ), + ) + .map((paramType, index) => { + let value = paramValues[`Parameter${index}`]; + + if (paramType.type === "signer") { + return signer; + } + if (paramType.type === "scriptAmountArray") { + value = paramValues[`Parameter${index}`] as ScriptAmountType[]; + return value.map((scriptAmount) => ({ + to: scriptAmount.script, + amount: scriptAmount.amount, + })); + } + // ... rest of existing param type handling ... + return value; + }); + + // Handle context parameters separately + methodParams.forEach((paramType, index) => { + const value = paramValues[`Parameter${index}`]; + if (paramType.type === "contextScript") { + context = { script: value as ccc.ScriptLike }; + } else if (paramType.type === "contextCell") { + context = { cell: value as ccc.CellLike }; + } else if (paramType.type === "contextTransaction") { + context = { transaction: value as ccc.TransactionLike }; + } + }); + + setSSRICallDetails({ + trait: rawMethodPath.split(".")[0], + method: rawMethodPath.split(".")[1], + args: args, + contractOutPoint: { + txHash: contractOutPointTx, + index: parseInt(contractOutPointIndex), + }, + ssriContext: context, + }); + + log( + "Calling", + rawMethodPath, + "on contract at", + String(contractOutPointTx), + "index", + String(contractOutPointIndex), + ); + let result; + if (rawMethodPath.split(".")[0] === "SSRI") { + // Type-safe way to call built-in methods + switch (rawMethodPath) { + case "SSRI.getMethods": + result = await contract.getMethods( + args[0] as number, + args[1] as number, + ); + break; + case "SSRI.hasMethods": + result = await contract.hasMethods( + (args[0] as string[]) ?? [], + (args[1] as ccc.HexLike[]) ?? [], + ); + break; + case "SSRI.version": + result = await contract.version(); + break; + } + } else { + let argsHex = methodParams.map((param, index) => { + const arg = args[index]; + + switch (param.type) { + case "contextScript": + case "contextCell": + case "contextTransaction": + // Context params are handled separately in context object + return "0x"; + + case "scriptAmountArray": + case "scriptArray": + // These are already properly formatted in the args preparation above + return ccc.hexFrom(JSON.stringify(arg)); + + case "tx": + // Transaction data should already be in hex format + return (arg as string) || "0x"; + + case "signer": + // Signer is handled specially in args preparation + return "0x"; + + case "hex": + // Single hex value, should already be 0x-prefixed + return arg as string; + + case "stringArray": + // Array of strings + return ccc.hexFrom(JSON.stringify(arg)); + + default: + throw new Error(`Unsupported parameter type: ${param.type}`); + } + }); + result = await contract + .assertExecutor() + .runScript(contract.code, rawMethodPath, argsHex); + } + if (result) { + if ( + rawMethodPath.split(".")[0] === "UDT" && + rawMethodPath.split(".")[1] === "icon" + ) { + const dataURL = ccc.bytesTo(result.res as string, "utf8"); + setMethodResult(result); + setIconDataURL(dataURL); + } else { + setMethodResult(result); + } + } + } catch (e) { + let errorMessage = + e instanceof Error + ? e.message + : typeof e === "object" + ? "Check your SSRI server" + : String(e) || "Unknown error"; + if (String(errorMessage).length < 3) { + errorMessage = + "Check your SSRI server or URL. Run `docker run -p 9090:9090 hanssen0/ckb-ssri-server` to start a local SSRI server."; + } + setMethodResult(`Error: ${errorMessage}`); + error(`Error: ${errorMessage}`); + } finally { + setIsLoading(false); + } + }; + + return ( +
+
+

+ How to Use: +

+
+
+ + 1 + +
+ + docker run -p 9090:9090 hanssen0/ckb-ssri-server + + to start a local SSRI server. +
+
+ +
+ + 2 + +
+ The default parameters are prepared to just work. Just click{" "} + + Execute Method + {" "} + button at the bottom to call the{" "} + + SSRI.version + {" "} + method. +
+
+ +
+ + 3 + +
+ All Done! You called an SSRI method! Try playing with other + methods while reading{" "} + + [EN/CN] Script-Sourced Rich Information - 来源于 Script 的富信息 + {" "} + to know how to adjust parameters to your need. +
+
+
+
+ <> + +
+ + +
+ { + const [tx, index] = value.split(":"); + setContractOutPointTx(tx || ""); + setContractOutPointIndex(index || "0"); + }, + ]} + /> +
+ + ({ + name: method, + displayName: method, + iconName: "Code", + }))} + selected={rawMethodPath} + onSelect={(value) => { + if (value !== "Customized") { + setRawMethodPath(value); + if (value === "SSRI.getMethods") { + setMethodParams([ + { name: "offset", type: "number" }, + { name: "limit", type: "number" }, + ]); + } else if (value === "SSRI.hasMethods") { + setMethodParams([ + { name: "methodNames", type: "stringArray" }, + { name: "extraMethodPaths", type: "hexArray" }, + ]); + } else { + setMethodParams([]); + } + } else { + setRawMethodPath(""); + } + }} + className="flex-1" + /> + <> + { + setRawMethodPath(value); + }, + ]} + className="flex-1" + /> + +
+ + +
+ + setSelectedParamType(value as MethodParamType)} + className="flex-grow" + /> + +
+ + {methodParams.map((param, index) => ( +
+
+ {param.type === "hex" ? ( +
+
+ +
+ { + if (!value.startsWith("0x")) value = "0x" + value; + setParamValues((prev) => ({ + ...prev, + [`Parameter${index}`]: value, + })); + }, + ]} + /> +
+ ) : param.type === "scriptAmountArray" || + param.type === "scriptArray" ? ( + + setParamValues((prev) => ({ + ...prev, + [`Parameter${index}`]: scriptAmounts, + })) + } + showAmount={param.type === "scriptAmountArray"} + /> + ) : param.type === "hexArray" ? ( + + setParamValues((prev) => ({ + ...prev, + [`Parameter${index}`]: hexValues, + })) + } + /> + ) : param.type == "signer" ? ( +
+
+ + {signer && ( + + )} + {!signer && ( + + )} +
+
+ ) : param.type == "contextScript" ? ( +
+
+ +
+
+ + setParamValues((prev) => ({ + ...prev, + [`Parameter${index}`]: { + ...((prev[`Parameter${index}`] as ccc.ScriptLike) || + {}), + codeHash: value, + }, + })), + ]} + /> +
+ + + setParamValues((prev) => ({ + ...prev, + [`Parameter${index}`]: { + ...((prev[`Parameter${index}`] as ccc.ScriptLike) || + {}), + hashType: value, + }, + })) + } + /> +
+ + setParamValues((prev) => ({ + ...prev, + [`Parameter${index}`]: { + ...((prev[`Parameter${index}`] as ccc.ScriptLike) || + {}), + args: value, + }, + })), + ]} + /> +
+
+ ) : param.type == "contextCell" ? ( +
+ + Parameter {index} ({param.type}) + +
+ + setParamValues((prev) => ({ + ...prev, + [`Parameter${index}`]: { + outPoint: { + txHash: "0x", + index: 0, + }, + cellOutput: { + capacity: value, + lock: { + codeHash: "", + hashType: "type" as const, + args: "", + }, + }, + outputData: + (prev[`Parameter${index}`] as ccc.CellLike) + ?.outputData || "", + } as ccc.CellLike, + })), + ]} + /> + + setParamValues((prev) => ({ + ...prev, + [`Parameter${index}`]: { + outPoint: { + txHash: "0x", + index: 0, + }, + cellOutput: { + capacity: + ( + prev[`Parameter${index}`] as ccc.CellLike + )?.cellOutput?.capacity?.toString() || "", + lock: { + codeHash: "", + hashType: "type" as const, + args: "", + }, + }, + outputData: value, + } as ccc.CellLike, + })), + ]} + /> +
+
+ ) : param.type == "contextTransaction" ? ( +
+ + Parameter {index} ({param.type}) + +
+ + setParamValues((prev) => ({ + ...prev, + [`Parameter${index}`]: value, + })), + ]} + /> +
+
+ ) : param.type == "tx" ? ( +
+
+ + +
+ {paramValues[`Parameter${index}NotUsingDefault`] && ( +
+ + setParamValues((prev) => ({ + ...prev, + [`Parameter${index}`]: value, + })), + ]} + /> +
+ )} +
+ ) : param.type === "stringArray" ? ( +
+
+ +
+ + setParamValues((prev) => ({ + ...prev, + [`Parameter${index}`]: value + .split(",") + .map((s) => s.trim()), + })), + ]} + /> +
+ ) : ( +
+
+ +
+ + setParamValues((prev) => ({ + ...prev, + [`Parameter${index}`]: value, + })), + ]} + /> +
+ )} + {param.type === "hexArray" && + rawMethodPath === "SSRI.hasMethods" && ( +
+ setMethodPathInput(value), + ]} + className="flex-grow" + /> + +
+ )} +
+ +
+ ))} + + <> +
+ setShowSSRICallDetails(e.target.checked)} + className="rounded border-gray-300" + /> + +
+ + {showSSRICallDetails && ( +
+ + {SSRICallDetails && ( +
+ +
+ )} +
+ )} + + +
+ + {isLoading ? ( +
+
+
+ ) : ( + methodResult && ( +
+ +
+ ) + )} +
+ {!isLoading && iconDataURL && ( +
+ + {""} +
+ )} + + + +
+ ); +} + +const methodParamTypeMap: Record<`${string}.${string}`, MethodParamType> = { + "name.context": "contextScript", + "symbol.context": "contextScript", + "decimals.context": "contextScript", + "totalSupply.context": "contextScript", + "balanceOf.context": "contextScript", + "icon.context": "contextScript", + "transfer.signer": "signer", + "transfer.transfers": "scriptAmountArray", + "transfer.tx": "tx", + "mint.signer": "signer", + "mint.mints": "scriptAmountArray", + "mint.tx": "tx", + "pause.signer": "signer", + "pause.locks": "scriptArray", + "pause.tx": "tx", + "pause.extraLockHashes": "hexArray", + "unpause.signer": "signer", + "unpause.locks": "scriptArray", + "unpause.tx": "tx", + "unpause.extraLockHashes": "hexArray", + "isPaused.locks": "scriptArray", + "isPaused.extraLockHashes": "hexArray", + "SSRI.hasMethods.methodNames": "stringArray", + "SSRI.hasMethods.extraMethodPaths": "hexArray", +}; + +const hiddenMethods = [ + "constructor", + "completeChangeToLock", + "completeBy", + "assertExecutor", + "tryRun", + "hasMethods", + "getMethods", + "version", +]; diff --git a/packages/demo/src/app/connected/(tools)/UDT/page.tsx b/packages/demo/src/app/connected/(tools)/UDT/page.tsx new file mode 100644 index 00000000..5c04ad4a --- /dev/null +++ b/packages/demo/src/app/connected/(tools)/UDT/page.tsx @@ -0,0 +1,1002 @@ +"use client"; + +import "reflect-metadata"; +import React, { useState, useEffect, useCallback } from "react"; +import { Button } from "@/src/components/Button"; +import { TextInput } from "@/src/components/Input"; +import { useApp } from "@/src/context"; +import { ButtonsPanel } from "@/src/components/ButtonsPanel"; +import { Dropdown } from "@/src/components/Dropdown"; +import { + ScriptAmountArrayInput, + ScriptAmountType, +} from "@/src/components/ScriptAmountInput"; +import { ssri } from "@ckb-ccc/ssri"; +import { udt } from "@ckb-ccc/udt"; +import { ccc } from "@ckb-ccc/connector-react"; +import JsonView from "@uiw/react-json-view"; +import { darkTheme } from "@uiw/react-json-view/dark"; +import Image from "next/image"; +import { HexArrayInput } from "@/src/components/HexArrayInput"; + +type ParamValue = + | string + | ScriptAmountType[] + | ccc.ScriptLike + | ccc.ScriptLike[] + | ccc.CellLike + | ccc.TransactionLike + | boolean + | undefined + | ccc.HexLike[]; + +type MethodParamType = + | "contextScript" + | "contextCell" + | "contextTransaction" + | "scriptAmountArray" + | "scriptArray" + | "tx" + | "signer" + | "hexArray" + | "number"; + +interface MethodParam { + name: string; + type?: MethodParamType; +} + +type ScriptContext = { + codeHash: string; + hashType: string; + args: string; +}; + +type MethodDefinition = { + params: MethodParam[]; + trait: "UDT" | "UDTPausable" | "both"; +}; + +export default function UDT() { + const { signer, createSender } = useApp(); + const { log, error } = createSender("UDT"); + + const [SSRIExecutorURL, setSSRIExecutorURL] = useState( + "http://localhost:9090", + ); + const [contractOutPointTx, setContractOutPointTx] = useState(""); + const [contractOutPointIndex, setContractOutPointIndex] = + useState("0"); + const [activeTrait, setActiveTrait] = useState<"UDT" | "UDTPausable">("UDT"); + const [methodList, setMethodList] = useState([]); + const [activeMethod, setActiveMethod] = useState("name"); + const [methodParams, setMethodParams] = useState([]); + const [paramValues, setParamValues] = useState>( + {}, + ); + const [methodResult, setMethodResult] = useState(undefined); + const [UDTCallDetails, setUDTCallDetails] = useState(null); + const [iconDataURL, setIconDataURL] = useState(""); + const [udtType, setUdtType] = useState<"ssri" | "xudt">("ssri"); + const [availableTraits, setAvailableTraits] = useState< + ("UDT" | "UDTPausable")[] + >([]); + const [udtScriptArgs, setUdtScriptArgs] = useState( + "0x02c93173368ec56f72ec023f63148461b80e7698eddd62cbd9dbe31a13f2b330", + ); + const [udtContractTypeIDArgs, setUdtContractTypeIDArgs] = useState( + "0x8fd55df879dc6176c95f3c420631f990ada2d4ece978c9512c39616dead2ed56", + ); + const [showUDTCallDetails, setShowUDTCallDetails] = useState(false); + const [isLoading, setIsLoading] = useState(false); + + useEffect(() => { + if (udtType === "ssri") { + setAvailableTraits(["UDT", "UDTPausable"]); + } else if (udtType === "xudt") { + setAvailableTraits(["UDT"]); + } + }, [udtType]); + + useEffect(() => { + if (activeTrait === "UDT") { + // Get all methods including inherited ones by walking up the prototype chain + const getAllMethods = (obj: any): string[] => { + const methods = new Set(); + let currentObj = obj; + + while (currentObj && currentObj !== Object.prototype) { + Object.getOwnPropertyNames(currentObj) + .filter( + (name) => + name !== "constructor" && + typeof currentObj[name] === "function", + ) + .forEach((name) => methods.add(name)); + + currentObj = Object.getPrototypeOf(currentObj); + } + + return Array.from(methods); + }; + + const methods = getAllMethods(udt.Udt.prototype).filter( + (method) => !hiddenMethods.includes(method), + ); + setMethodList(methods); + if (!methods.includes(activeMethod)) { + setActiveMethod(methods[0] || ""); + } + } else if (activeTrait === "UDTPausable") { + // Apply the same pattern for UDTPausable + const getAllMethods = (obj: any): string[] => { + const methods = new Set(); + let currentObj = obj; + + while (currentObj && currentObj !== Object.prototype) { + Object.getOwnPropertyNames(currentObj) + .filter( + (name) => + name !== "constructor" && + typeof currentObj[name] === "function", + ) + .forEach((name) => methods.add(name)); + + currentObj = Object.getPrototypeOf(currentObj); + } + + return Array.from(methods); + }; + + const methods = getAllMethods(udt.UdtPausable.prototype).filter( + (method) => !hiddenMethods.includes(method), + ); + setMethodList(methods); + if (!methods.includes(activeMethod)) { + setActiveMethod(methods[0] || ""); + } + } + }, [activeMethod, activeTrait]); + + useEffect(() => { + if (activeMethod) { + const params = getMethodParameters(activeTrait, activeMethod); + setMethodParams(params); + setParamValues({}); + } + }, [activeMethod, activeTrait]); + + const getOutPointFromTypeIDArgs = useCallback(async () => { + if (!signer) return; + const scriptCell = await signer.client.findSingletonCellByType({ + codeHash: "0x00000000000000000000000000000000000000000000000000545950455f4944", + hashType: "type", + args: udtContractTypeIDArgs, + }); + if (!scriptCell) { + throw new Error("PUDT script cell not found"); + } + const targetOutPoint = scriptCell.outPoint; + setContractOutPointTx(targetOutPoint.txHash); + setContractOutPointIndex(targetOutPoint.index.toString()); + }, [signer, udtContractTypeIDArgs]); + + useEffect(() => { + getOutPointFromTypeIDArgs(); + }, [udtContractTypeIDArgs, signer, getOutPointFromTypeIDArgs]); + + const makeUDTCall = async () => { + if (!signer) return; + + // Set loading state and clear previous results + setIsLoading(true); + setMethodResult(undefined); + setIconDataURL(""); + + const ssriExecutor = new ssri.ExecutorJsonRpc(SSRIExecutorURL); + + let contract: udt.Udt | udt.UdtPausable | undefined; + try { + const targetOutPoint = { + txHash: contractOutPointTx, + index: parseInt(contractOutPointIndex), + }; + const scriptCell = await signer.client.getCell(targetOutPoint); + + if (!scriptCell) { + throw new Error("Script cell not found"); + } + + if (!scriptCell.cellOutput.type?.hash()) { + throw new Error("Script cell type hash not found"); + } + + const type = ccc.Script.from({ + codeHash: scriptCell.cellOutput.type?.hash(), + hashType: "type", + args: udtScriptArgs, + }); + + if (activeTrait === "UDT") { + contract = new udt.Udt(targetOutPoint, type, { + executor: ssriExecutor, + }); + } else if (activeTrait === "UDTPausable") { + contract = new udt.UdtPausable(targetOutPoint, type, { + executor: ssriExecutor, + }); + } else if (udtType === "xudt") { + const script = await ccc.Script.fromKnownScript( + signer.client, + ccc.KnownScript.XUdt, + udtScriptArgs, + ); + + const scriptInfo = await signer.client.getKnownScript( + ccc.KnownScript.XUdt, + ); + + contract = new udt.Udt( + scriptInfo.cellDeps[0].cellDep.outPoint, + script, + { executor: ssriExecutor }, + ); + } + + // Check contract is defined before using + if (!contract) { + throw new Error("Contract not initialized"); + } + + const args = methodParams.map((paramType, index) => { + let value = paramValues[`Parameter$[index}`]; + if (paramType.type === "contextScript") { + if (!paramValues[`Parameter$[index}NotUsingDefault`]) { + value = contract?.script; + } + return { + script: value as ccc.ScriptLike, + }; + } + if (paramType.type === "signer") { + return signer; + } + if (paramType.type === "scriptAmountArray") { + value = paramValues[`Parameter$[index}`] as ScriptAmountType[]; + const scriptAmountArray = value.map((scriptAmount) => ({ + to: scriptAmount.script, + amount: scriptAmount.amount, + })); + return scriptAmountArray; + } + if (paramType.type === "hexArray") { + // Ensure value is parsed from JSON string + const parsedArray = + typeof value === "string" + ? JSON.parse(value) + : Array.isArray(value) + ? value + : []; + + return parsedArray; + } + const isTransaction = + paramType.name?.toLowerCase().includes("tx") || + paramType.name?.toLowerCase().includes("transaction"); + if (isTransaction && value) { + const hexData = value as string; + if (hexData && !hexData.startsWith("0x")) { + throw new Error("Transaction data must be 0x-prefixed hex string"); + } + return ccc.Transaction.fromBytes(ccc.bytesFrom(hexData)); + } + return value; + }); + + setUDTCallDetails({ + trait: activeTrait, + method: activeMethod, + args: args, + contractOutPoint: { + txHash: contractOutPointTx, + index: parseInt(contractOutPointIndex), + }, + }); + + log( + "Calling method", + String(activeMethod), + "on contract", + String(activeTrait), + "at", + String(contractOutPointTx), + "index", + String(contractOutPointIndex), + ); + + let result; + if (methodParams.length === 0) { + result = await (contract as any)[activeMethod](); + } else { + result = await (contract as any)[activeMethod](...args); + } + + // Check if result contains a transaction to send + if (result?.res instanceof ccc.Transaction) { + log("Sending transaction..."); + let transaction = result.res; + if (activeMethod === "transfer") { + transaction = await contract.completeBy(result.res, signer); + } + await transaction.completeFeeBy(signer); + const hash = await signer.sendTransaction(transaction); + result.txHash = hash; + log("Transaction sent with hash:", hash); + } + + if (activeTrait === "UDT" && activeMethod === "icon") { + const dataURL = result.res; + setMethodResult(result); + setIconDataURL(dataURL); + } else { + setMethodResult(result); + } + } catch (e) { + let errorMessage = + e instanceof Error + ? e.message + : typeof e === "object" + ? "Check your SSRI server" + : String(e) || "Unknown error"; + if (String(errorMessage).length < 3) { + errorMessage = + "Check your SSRI server or URL. Run `docker run -p 9090:9090 hanssen0/ckb-ssri-server` to start a local SSRI server."; + } + setMethodResult(`Error: ${errorMessage}`); + error(`Error: ${errorMessage}`); + } finally { + setIsLoading(false); + } + }; + + return ( +
+
+

+ How to Use: +

+
+
+ + 1 + +
+ + docker run -p 9090:9090 hanssen0/ckb-ssri-server + + to start a local SSRI server. +
+
+ +
+ + 2 + +
+ The default parameters are prepared to just work. Just click{" "} + + Execute Method + {" "} + button at the bottom to call the{" "} + + UDT.name + {" "} + method. +
+
+ +
+ + 3 + +
+ All Done! You called a UDT method! Try playing with other methods + while reading{" "} + + CCC's Support for User Defined Token (UDT) + {" "} + to know how to adjust parameters to your need. +
+
+
+
+ + +
+ + + {/* */} +
+ + +
+
+ + +
+ {paramValues.udtScriptArgsNotUsingDefault && ( +
+ +
+ )} +
+ +
+
+ + ({ + name: trait, + displayName: trait, + iconName: "Coins", + }))} + selected={activeTrait} + onSelect={(item) => setActiveTrait(item as "UDT" | "UDTPausable")} + className="flex-grow" + /> +
+ +
+ + ({ + name: method, + displayName: method, + iconName: "Coins", + }))} + selected={ + activeMethod || (methodList.length > 0 ? methodList[0] : "") + } + onSelect={setActiveMethod} + className="flex-grow" + /> +
+
+ + {methodParams.map((paramType, index) => { + if ( + paramType.type === "scriptAmountArray" || + paramType.type === "scriptArray" + ) { + return ( + + setParamValues((prev) => ({ + ...prev, + [`Parameter$[index}`]: + paramType.type === "scriptArray" + ? scriptAmounts.map((item) => item.script) + : scriptAmounts, + })) + } + showAmount={paramType.type === "scriptAmountArray"} + /> + ); + } + + if (paramType.type === "hexArray") { + return ( + + setParamValues((prev) => ({ + ...prev, + [`Parameter$[index}`]: hexValues, + })) + } + /> + ); + } + + if (paramType.type == "signer") { + return ( +
+
+ + {signer && ( + + )} + {!signer && ( + + )} +
+
+ ); + } + + if (paramType.type == "contextScript") { + return ( +
+
+ + +
+ + {paramValues[`Parameter$[index}NotUsingDefault`] && ( +
+ + setParamValues((prev) => ({ + ...prev, + [`Parameter$[index}`]: { + ...((prev[`Parameter$[index}`] as ccc.ScriptLike) || + {}), + codeHash: value, + }, + })), + ]} + /> +
+ + + setParamValues((prev) => ({ + ...prev, + [`Parameter$[index}`]: { + ...((prev[`Parameter$[index}`] as ccc.ScriptLike) || + {}), + hashType: value, + }, + })) + } + /> +
+ + setParamValues((prev) => ({ + ...prev, + [`Parameter$[index}`]: { + ...((prev[`Parameter$[index}`] as ccc.ScriptLike) || + {}), + args: value, + }, + })), + ]} + /> +
+ )} +
+ ); + } + + if (paramType.type == "contextCell") { + return ( +
+ + Parameter {index} - {paramType.name}({paramType.type}) + +
+ + setParamValues((prev) => ({ + ...prev, + [`Parameter$[index}`]: { + outPoint: { + txHash: "0x", + index: 0, + }, + cellOutput: { + capacity: value, + lock: { + codeHash: "", + hashType: "type" as const, + args: "", + }, + }, + outputData: + (prev[`Parameter$[index}`] as ccc.CellLike) + ?.outputData || "", + } as ccc.CellLike, + })), + ]} + /> + + setParamValues((prev) => ({ + ...prev, + [`Parameter$[index}`]: { + outPoint: { + txHash: "0x", + index: 0, + }, + cellOutput: { + capacity: + ( + prev[`Parameter$[index}`] as ccc.CellLike + )?.cellOutput?.capacity?.toString() || "", + lock: { + codeHash: "", + hashType: "type" as const, + args: "", + }, + }, + outputData: value, + } as ccc.CellLike, + })), + ]} + /> +
+
+ ); + } + + if (paramType.type == "tx") { + return ( +
+
+ + +
+ {paramValues[`Parameter$[index}NotUsingDefault`] && ( +
+ + setParamValues((prev) => ({ + ...prev, + [`Parameter$[index}`]: value, + })), + ]} + /> +
+ )} +
+ ); + } + + return ( + + setParamValues((prev) => ({ + ...prev, + [`Parameter$[index}`]: value, + })), + ]} + /> + ); + })} + + {udtType === "ssri" && ( + <> +
+ setShowUDTCallDetails(e.target.checked)} + className="rounded border-gray-300" + /> + +
+ + {showUDTCallDetails && ( +
+ + {UDTCallDetails && ( +
+ +
+ )} +
+ )} + + )} + +
+ + {isLoading ? ( +
+
+
+ ) : ( + methodResult && ( +
+ +
+ ) + )} +
+ {!isLoading && iconDataURL && ( +
+ + {""} +
+ )} + + + +
+ ); +} + +const methodDefinitions: Record = { + name: { + params: [{ name: "context", type: "contextScript" }], + trait: "both", + }, + symbol: { + params: [{ name: "context", type: "contextScript" }], + trait: "both", + }, + decimals: { + params: [{ name: "context", type: "contextScript" }], + trait: "both", + }, + icon: { + params: [{ name: "context", type: "contextScript" }], + trait: "both", + }, + transfer: { + params: [ + { name: "signer", type: "signer" }, + { name: "transfers", type: "scriptAmountArray" }, + { name: "tx", type: "tx" }, + ], + trait: "both", + }, + mint: { + params: [ + { name: "signer", type: "signer" }, + { name: "mints", type: "scriptAmountArray" }, + { name: "tx", type: "tx" }, + ], + trait: "both", + }, + pause: { + params: [ + { name: "signer", type: "signer" }, + { name: "locks", type: "scriptArray" }, + { name: "tx", type: "tx" }, + { name: "extraLockHashes", type: "hexArray" }, + ], + trait: "UDTPausable", + }, + unpause: { + params: [ + { name: "signer", type: "signer" }, + { name: "locks", type: "scriptArray" }, + { name: "tx", type: "tx" }, + { name: "extraLockHashes", type: "hexArray" }, + ], + trait: "UDTPausable", + }, + isPaused: { + params: [ + { name: "locks", type: "scriptArray" }, + { name: "extraLockHashes", type: "hexArray" }, + ], + trait: "UDTPausable", + }, + enumeratePaused: { + params: [ + { name: "offset", type: "number" }, + { name: "limit", type: "number" }, + ], + trait: "UDTPausable", + }, +}; + +const getMethodParameters = ( + trait: "UDT" | "UDTPausable", + methodName: string, +): MethodParam[] => { + const methodDef = methodDefinitions[methodName]; + if (!methodDef) return []; + + // Only return params if the method belongs to the current trait + // or is available in both traits + if (methodDef.trait === "both" || methodDef.trait === trait) { + return methodDef.params; + } + + return []; +}; + +const hiddenMethods = [ + "constructor", + "completeChangeToLock", + "completeBy", + "assertExecutor", + "tryRun", + "hasMethods", + "getMethods", + "version", +]; diff --git a/packages/demo/src/app/connected/(tools)/layout.tsx b/packages/demo/src/app/connected/(tools)/layout.tsx index 6632a401..7af75786 100644 --- a/packages/demo/src/app/connected/(tools)/layout.tsx +++ b/packages/demo/src/app/connected/(tools)/layout.tsx @@ -8,7 +8,7 @@ export default function RootLayout({ children: React.ReactNode; }>) { return ( -
+
{children}
); diff --git a/packages/demo/src/app/connected/page.tsx b/packages/demo/src/app/connected/page.tsx index f97b02a8..5fa9a174 100644 --- a/packages/demo/src/app/connected/page.tsx +++ b/packages/demo/src/app/connected/page.tsx @@ -47,6 +47,8 @@ const TABS: [ReactNode, string, keyof typeof icons, string][] = [ "text-cyan-600", ], ["Nervos DAO", "/connected/NervosDao", "Vault", "text-pink-500"], + ["UDT", "/connected/UDT", "Coins", "text-green-500"], + ["SSRI", "/connected/SSRI", "Pill", "text-blue-500"], ["Hash", "/utils/Hash", "Barcode", "text-violet-500"], ["Mnemonic", "/utils/Mnemonic", "SquareAsterisk", "text-fuchsia-500"], ["Keystore", "/utils/Keystore", "Notebook", "text-rose-500"], diff --git a/packages/demo/src/components/HexArrayInput.tsx b/packages/demo/src/components/HexArrayInput.tsx new file mode 100644 index 00000000..21a1734c --- /dev/null +++ b/packages/demo/src/components/HexArrayInput.tsx @@ -0,0 +1,93 @@ +"use client"; + +import { TextInput } from "@/src/components/Input"; +import { Button } from "@/src/components/Button"; + + +interface HexInputProps { + value: string; + onChange: (value: string) => void; + onRemove?: () => void; +} + +interface HexArrayInputProps { + value: string[]; + onChange: (value: string[]) => void; + label?: string; +} + +export const HexInput: React.FC = ({ + value, + onChange, + onRemove, +}) => { + return ( +
+ { + // Ensure hex format + const hexValue = newValue.startsWith("0x") + ? newValue + : `0x${newValue}`; + onChange(hexValue); + }, + ]} + className="w-full" + /> + {onRemove && ( + + )} +
+ ); +}; + +export const HexArrayInput: React.FC = ({ + value = [], + onChange, + label = "Hex Values", +}) => { + const addHexValue = () => { + onChange([...value, "0x"]); + }; + + const removeHexValue = (index: number) => { + const newValues = [...value]; + newValues.splice(index, 1); + onChange(newValues); + }; + + const updateHexValue = (index: number, hexValue: string) => { + const newValues = [...value]; + newValues[index] = hexValue; + onChange(newValues); + }; + + return ( +
+ + {value.map((hexValue, index) => ( + updateHexValue(index, updatedValue)} + onRemove={() => removeHexValue(index)} + /> + ))} + +
+ ); +}; \ No newline at end of file diff --git a/packages/demo/src/components/ScriptAmountInput.tsx b/packages/demo/src/components/ScriptAmountInput.tsx new file mode 100644 index 00000000..c90dd4b9 --- /dev/null +++ b/packages/demo/src/components/ScriptAmountInput.tsx @@ -0,0 +1,202 @@ +import React, { useState, useEffect } from "react"; +import { Button } from "@/src/components/Button"; +import { TextInput } from "@/src/components/Input"; +import { useApp } from "@/src/context"; +import { Dropdown } from "@/src/components/Dropdown"; +import { ccc } from "@ckb-ccc/connector-react"; +import { Icon } from "./Icon"; + +export type ScriptType = { + codeHash: string; + hashType: string; + args: string; +}; + +export type ScriptAmountType = { + script: ScriptType; + amount?: string; +}; + +export interface ScriptAmountArrayInputProps { + value: ScriptAmountType[]; + onChange: (value: ScriptAmountType[]) => void; + label?: string; + showAmount?: boolean; +} + +export interface ScriptAmountInputProps { + value: ScriptAmountType; + onChange: (value: ScriptAmountType) => void; + onRemove?: () => void; + showAmount?: boolean; +} + +export const ScriptAmountInput: React.FC = ({ + value, + onChange, + onRemove, + showAmount = true, +}) => { + const [inputType, setInputType] = useState<"script" | "address">("address"); + const [address, setAddress] = useState(""); + const { signer } = useApp(); + + // Handle address to script conversion + const handleAddressChange = async (newAddress: string) => { + setAddress(newAddress); + if (signer && newAddress) { + try { + const script = (await ccc.Address.fromString(newAddress, signer.client)) + .script; + onChange({ + ...value, + script: { + codeHash: script.codeHash, + hashType: script.hashType, + args: script.args, + }, + }); + } catch (error) { + console.error("Failed to parse address:", error); + } + } + }; + + return ( +
+
+ + setInputType(type as "script" | "address")} + className="flex-grow" + /> +
+ + {inputType === "address" ? ( + + ) : ( + <> + + onChange({ ...value, script: { ...value.script, codeHash } }), + ]} + className="w-full" + /> +
+ + + onChange({ ...value, script: { ...value.script, hashType } }) + } + className="flex-grow" + /> +
+ + onChange({ ...value, script: { ...value.script, args } }), + ]} + className="w-full" + /> + + )} + + {showAmount && ( + onChange({ ...value, amount }), + ]} + className="w-full" + /> + )} + {onRemove && ( + + )} +
+ ); +}; + +export const ScriptAmountArrayInput: React.FC = ({ + value = [], + onChange, + label = "Scripts with Amounts", + showAmount = true, +}) => { + const addScriptAmount = () => { + const newScript = { + script: { codeHash: "", hashType: "type", args: "" }, + ...(showAmount && { amount: "0" }), + }; + onChange([...value, newScript]); + }; + + const removeScriptAmount = (index: number) => { + const newScriptAmounts = [...value]; + newScriptAmounts.splice(index, 1); + onChange(newScriptAmounts); + }; + + const updateScriptAmount = ( + index: number, + scriptAmount: ScriptAmountType, + ) => { + const newScriptAmounts = [...value]; + newScriptAmounts[index] = scriptAmount; + onChange(newScriptAmounts); + }; + + return ( +
+ + {value.map((scriptAmount, index) => ( + + updateScriptAmount(index, updatedScriptAmount) + } + onRemove={() => removeScriptAmount(index)} + showAmount={showAmount} + /> + ))} + +
+ ); +}; diff --git a/packages/demo/tsconfig.json b/packages/demo/tsconfig.json index a7cc17ad..c97e06b1 100644 --- a/packages/demo/tsconfig.json +++ b/packages/demo/tsconfig.json @@ -19,7 +19,9 @@ ], "paths": { "@/*": ["./*"] - } + }, + "emitDecoratorMetadata": true, + "experimentalDecorators": true }, "include": [ "next-env.d.ts", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 183dfde8..c5f45d10 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -315,6 +315,9 @@ importers: '@lit/react': specifier: ^1.0.5 version: 1.0.5(@types/react@18.3.3) + '@uiw/react-json-view': + specifier: 2.0.0-alpha.30 + version: 2.0.0-alpha.30(@babel/runtime@7.25.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) lucide-react: specifier: ^0.427.0 version: 0.427.0(react@18.3.1) @@ -327,6 +330,9 @@ importers: react-dom: specifier: ^18 version: 18.3.1(react@18.3.1) + reflect-metadata: + specifier: ^0.2.0 + version: 0.2.2 devDependencies: '@ckb-ccc/connector-react': specifier: workspace:* @@ -334,6 +340,12 @@ importers: '@ckb-ccc/lumos-patches': specifier: workspace:* version: link:../lumos-patches + '@ckb-ccc/ssri': + specifier: workspace:* + version: link:../ssri + '@ckb-ccc/udt': + specifier: workspace:* + version: link:../udt '@ckb-lumos/ckb-indexer': specifier: ^0.24.0-next.1 version: 0.24.0-next.2 @@ -510,7 +522,7 @@ importers: version: 10.1.3(chokidar@3.6.0)(typescript@5.5.4) '@nestjs/testing': specifier: ^10.0.0 - version: 10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1)) + version: 10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1)(@nestjs/platform-express@10.4.1) '@types/express': specifier: ^4.17.17 version: 4.17.21 @@ -1886,6 +1898,7 @@ packages: engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [glibc] '@next/swc-linux-arm64-musl@14.2.10': resolution: {integrity: sha512-n2i5o3y2jpBfXFRxDREr342BGIQCJbdAUi/K4q6Env3aSx8erM9VuKXHw5KNROK9ejFSPf0LhoSkU/ZiNdacpQ==} @@ -1899,6 +1912,7 @@ packages: engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [musl] '@next/swc-linux-x64-gnu@14.2.10': resolution: {integrity: sha512-GXvajAWh2woTT0GKEDlkVhFNxhJS/XdDmrVHrPOA83pLzlGPQnixqxD8u3bBB9oATBKB//5e4vpACnx5Vaxdqg==} @@ -1912,6 +1926,7 @@ packages: engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [glibc] '@next/swc-linux-x64-musl@14.2.10': resolution: {integrity: sha512-opFFN5B0SnO+HTz4Wq4HaylXGFV+iHrVxd3YvREUX9K+xfc4ePbRrxqOuPOFjtSuiVouwe6uLeDtabjEIbkmDA==} @@ -1925,6 +1940,7 @@ packages: engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [musl] '@next/swc-win32-arm64-msvc@14.2.10': resolution: {integrity: sha512-9NUzZuR8WiXTvv+EiU/MXdcQ1XUvFixbLIMNQiVHuzs7ZIFrJDLJDaOF1KaqttoTujpcxljM/RNAOmw1GhPPQQ==} @@ -2336,6 +2352,13 @@ packages: resolution: {integrity: sha512-c6EIQRHhcpl6+tO8EMR+kjkkV+ugUNXOmeASA1rlzkd8EPIriavpWoiEz1HR/VLhbVIdhqnV6E7JZm00cBDx2A==} engines: {node: ^16.0.0 || >=18.0.0} + '@uiw/react-json-view@2.0.0-alpha.30': + resolution: {integrity: sha512-ufvvirUQcITU9s4R12b7hn/t7ngLCYp1KbBxE+eAD35o3Ey+uxfKvgWmIwGFhV3hFXXxMJ8SHQKwl/ywNCHsDA==} + peerDependencies: + '@babel/runtime': '>=7.10.0' + react: '>=18.0.0' + react-dom: '>=18.0.0' + '@ungap/structured-clone@1.2.0': resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} @@ -7099,7 +7122,7 @@ snapshots: transitivePeerDependencies: - chokidar - '@nestjs/testing@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1))': + '@nestjs/testing@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1)(@nestjs/platform-express@10.4.1)': dependencies: '@nestjs/common': 10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1) '@nestjs/core': 10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.1)(reflect-metadata@0.2.2)(rxjs@7.8.1) @@ -7679,6 +7702,12 @@ snapshots: '@typescript-eslint/types': 7.2.0 eslint-visitor-keys: 3.4.3 + '@uiw/react-json-view@2.0.0-alpha.30(@babel/runtime@7.25.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.25.6 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + '@ungap/structured-clone@1.2.0': {} '@webassemblyjs/ast@1.12.1': @@ -8785,8 +8814,8 @@ snapshots: '@typescript-eslint/parser': 7.2.0(eslint@8.57.0)(typescript@5.5.4) eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0) - eslint-plugin-import: 2.29.1(eslint@8.57.0) + eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.57.0))(eslint@8.57.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) eslint-plugin-jsx-a11y: 6.9.0(eslint@8.57.0) eslint-plugin-react: 7.35.0(eslint@8.57.0) eslint-plugin-react-hooks: 4.6.2(eslint@8.57.0) @@ -8831,12 +8860,29 @@ snapshots: transitivePeerDependencies: - supports-color + eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.57.0))(eslint@8.57.0): + dependencies: + debug: 4.3.6 + enhanced-resolve: 5.17.1 + eslint: 8.57.0 + eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) + fast-glob: 3.3.2 + get-tsconfig: 4.7.6 + is-core-module: 2.15.0 + is-glob: 4.0.3 + transitivePeerDependencies: + - '@typescript-eslint/parser' + - eslint-import-resolver-node + - eslint-import-resolver-webpack + - supports-color + eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0): dependencies: debug: 4.3.6 enhanced-resolve: 5.17.1 eslint: 8.57.0 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0) + eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) fast-glob: 3.3.2 get-tsconfig: 4.7.6 @@ -8848,7 +8894,18 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-module-utils@2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0): + eslint-module-utils@2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0): + dependencies: + debug: 3.2.7 + optionalDependencies: + '@typescript-eslint/parser': 7.2.0(eslint@8.57.0)(typescript@5.5.4) + eslint: 8.57.0 + eslint-import-resolver-node: 0.3.9 + eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.57.0))(eslint@8.57.0) + transitivePeerDependencies: + - supports-color + + eslint-module-utils@2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0): dependencies: debug: 3.2.7 optionalDependencies: @@ -8859,7 +8916,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0): + eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0): dependencies: array-includes: 3.1.8 array.prototype.findlastindex: 1.2.5 @@ -8869,7 +8926,7 @@ snapshots: doctrine: 2.1.0 eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0) + eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) hasown: 2.0.2 is-core-module: 2.15.0 is-glob: 4.0.3 @@ -8886,7 +8943,7 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-plugin-import@2.29.1(eslint@8.57.0): + eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0): dependencies: array-includes: 3.1.8 array.prototype.findlastindex: 1.2.5 @@ -8896,7 +8953,7 @@ snapshots: doctrine: 2.1.0 eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0) + eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) hasown: 2.0.2 is-core-module: 2.15.0 is-glob: 4.0.3 @@ -8906,6 +8963,8 @@ snapshots: object.values: 1.2.0 semver: 6.3.1 tsconfig-paths: 3.15.0 + optionalDependencies: + '@typescript-eslint/parser': 7.2.0(eslint@8.57.0)(typescript@5.5.4) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack