@@ -406,6 +537,7 @@ export function CommunityDeploymentPanel({
!factoryId ||
networkMismatch ||
walletNetworkUnknown ||
+ authorization !== "ready" ||
stage === "simulating"
}
className="min-h-11 rounded-lg border border-indigo-500 px-4 py-2 text-sm font-medium text-indigo-200 hover:bg-indigo-950/50 disabled:cursor-not-allowed disabled:opacity-50"
@@ -421,6 +553,7 @@ export function CommunityDeploymentPanel({
!confirmed ||
networkMismatch ||
walletNetworkUnknown ||
+ authorization !== "ready" ||
stage === "awaiting_approval"
}
className="min-h-11 rounded-lg bg-indigo-500 px-5 py-2 text-sm font-medium text-white hover:bg-indigo-400 disabled:cursor-not-allowed disabled:opacity-50"
diff --git a/apps/web/src/lib/community/deployment.ts b/apps/web/src/lib/community/deployment.ts
index 9818891..97ebeea 100644
--- a/apps/web/src/lib/community/deployment.ts
+++ b/apps/web/src/lib/community/deployment.ts
@@ -74,6 +74,13 @@ export type CommunityDeploymentAdapter = {
verifyRegistry(
expected: CommunityRegistryRecord,
): Promise<"verified" | "missing" | "mismatch" | "rpc-error">;
+ /**
+ * Reads the CommunityFactory owner so the UI can gate deployment before any
+ * simulation or signature. Resolves to the owner address string on success;
+ * throws on a transient read failure so callers can retry rather than treat
+ * the result as an authorization verdict.
+ */
+ readFactoryOwner(factoryId: string, publicKey: string): Promise;
};
function bytesToHex(bytes: Uint8Array): string {
@@ -355,6 +362,29 @@ export const defaultCommunityDeploymentAdapter: CommunityDeploymentAdapter = {
return "rpc-error";
}
},
+
+ /**
+ * Reads `owner()` off the factory as a read-only simulation. Any throw is a
+ * transient read failure that the UI reports as retryable, never as
+ * "unauthorized".
+ */
+ async readFactoryOwner(factoryId, publicKey) {
+ const transaction = await AssembledTransaction.build({
+ contractId: factoryId,
+ method: "owner",
+ args: [],
+ networkPassphrase: config.networkPassphrase,
+ rpcUrl: config.rpcUrl,
+ publicKey,
+ timeoutInSeconds: COMMUNITY_DEPLOYMENT_TIMEOUT_SECONDS,
+ parseResultXdr: (value) => scValToNative(value) as string,
+ });
+ const owner = transaction.result;
+ if (typeof owner !== "string" || owner.trim() === "") {
+ throw new Error("The CommunityFactory owner read did not return an address.");
+ }
+ return owner;
+ },
};
export function metadataHashBytes(invocation: CommunityFactoryInvocation) {
diff --git a/apps/web/src/lib/communityFactory/useCommunityDeployment.ts b/apps/web/src/lib/communityFactory/useCommunityDeployment.ts
index 11b4cf8..5a185e3 100644
--- a/apps/web/src/lib/communityFactory/useCommunityDeployment.ts
+++ b/apps/web/src/lib/communityFactory/useCommunityDeployment.ts
@@ -7,7 +7,7 @@ import {
getStoredCommunityDeploymentHash,
storeCommunityDeploymentHash,
} from "@/lib/contracts";
-import { activeCapabilities } from "@/lib/stellar";
+import { config } from "@/lib/stellar";
import {
deployCommunityFromWizard,
type DeploymentStage,
@@ -54,8 +54,7 @@ export function useCommunityDeployment() {
try {
const nextOutcome = await deployCommunityFromWizard(state, {
address,
- expectedNetworkPassphrase:
- activeCapabilities.network.networkPassphrase,
+ expectedNetworkPassphrase: config.networkPassphrase,
walletNetworkPassphrase,
createClient: () =>
createCommunityFactoryClient({