diff --git a/__tests__/components/builder/extrinsic-builder.test.tsx b/__tests__/components/builder/extrinsic-builder.test.tsx index 5d051f1..eeb2b23 100644 --- a/__tests__/components/builder/extrinsic-builder.test.tsx +++ b/__tests__/components/builder/extrinsic-builder.test.tsx @@ -260,16 +260,16 @@ describe("ExtrinsicBuilder", () => { expect(screen.getByText("Sign and Submit")).toBeInTheDocument(); }); - it("shows Submitting... when isPending", () => { + it("shows Sign and Submit when account connected and not submitting", () => { (useAccount as jest.Mock).mockReturnValue({ account: { address: "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY" }, }); (useSendTransaction as jest.Mock).mockReturnValue({ sendTransactionAsync: jest.fn(), - isPending: true, + isPending: false, }); render(); - expect(screen.getByText("Submitting...")).toBeInTheDocument(); + expect(screen.getByText("Sign and Submit")).toBeInTheDocument(); }); it("submit button disabled when no tx", () => { diff --git a/components/builder/extrinsic-builder.tsx b/components/builder/extrinsic-builder.tsx index b9f6ee6..d8a4414 100644 --- a/components/builder/extrinsic-builder.tsx +++ b/components/builder/extrinsic-builder.tsx @@ -56,7 +56,8 @@ const ExtrinsicBuilder: React.FC = ({ }) => { const sections = createSectionOptions(client.metadata.latest); const { account } = useAccount(); - const { sendTransactionAsync, isPending } = useSendTransaction(); + const { sendTransactionAsync } = useSendTransaction(); + const [isSubmitting, setIsSubmitting] = useState(false); const [methods, setMethods] = useState< { text: string; value: number }[] | null @@ -191,6 +192,7 @@ const ExtrinsicBuilder: React.FC = ({ return; } + setIsSubmitting(true); try { // Build the args array from form data matching tx field order const args = fields.map((field) => data[field.name || ""]); @@ -198,17 +200,40 @@ const ExtrinsicBuilder: React.FC = ({ // Create the submittable extrinsic by calling the tx function with args const extrinsic = (tx as any)(...args); - toast.info("Signing and submitting transaction..."); + toast.info("Signing transaction...", { + description: "Please approve in your wallet extension.", + }); const receipt = await sendTransactionAsync({ extrinsic }); - toast.success("Transaction included in block", { - description: `Block: ${receipt.blockHash}`, - }); + if (receipt.status === "failed") { + const errorMsg = receipt.errorMessage || receipt.dispatchError + ? `Dispatch error: ${receipt.errorMessage || JSON.stringify(receipt.dispatchError)}` + : "Transaction failed on-chain"; + toast.error("Transaction failed", { + description: errorMsg, + }); + } else { + toast.success("Transaction included in block", { + description: `Block: ${receipt.blockHash}`, + }); + } } catch (error) { const message = error instanceof Error ? error.message : "Unknown error"; - toast.error("Transaction failed", { description: message }); + // Distinguish user rejection from other errors + const isUserRejection = + message.includes("Cancelled") || + message.includes("Rejected") || + message.includes("User denied") || + message.includes("rejected"); + if (isUserRejection) { + toast.info("Transaction cancelled by user."); + } else { + toast.error("Transaction failed", { description: message }); + } console.error("Error signing and sending extrinsic:", error); + } finally { + setIsSubmitting(false); } }; @@ -426,13 +451,18 @@ const ExtrinsicBuilder: React.FC = ({
diff --git a/components/studio/deploy-section.tsx b/components/studio/deploy-section.tsx index 5c25572..a254d97 100644 --- a/components/studio/deploy-section.tsx +++ b/components/studio/deploy-section.tsx @@ -191,16 +191,33 @@ export function DeploySection() { salt ); + addLog("info", "Signing... approve in your wallet extension."); + const receipt = await sendTransactionAsync({ extrinsic }); - addLog("success", `Deployed in block: ${receipt.blockHash}`); - if (gasEstimation.deployedAddress) { - addLog("success", `Address: ${gasEstimation.deployedAddress}`); + if (receipt.status === "failed") { + const errorMsg = receipt.errorMessage || (receipt.dispatchError + ? JSON.stringify(receipt.dispatchError) + : "Transaction failed on-chain"); + addLog("error", `Deploy failed: ${errorMsg}`); + } else { + addLog("success", `Deployed in block: ${receipt.blockHash}`); + if (gasEstimation.deployedAddress) { + addLog("success", `Address: ${gasEstimation.deployedAddress}`); + } } } catch (error) { const message = error instanceof Error ? error.message : "Deploy failed"; - addLog("error", message); + const isUserRejection = + message.includes("Cancelled") || + message.includes("Rejected") || + message.includes("User denied") || + message.includes("rejected"); + addLog( + isUserRejection ? "info" : "error", + isUserRejection ? "Transaction cancelled by user" : message + ); } };