diff --git a/backend/tally/settings.py b/backend/tally/settings.py index b50ed15..a84d177 100644 --- a/backend/tally/settings.py +++ b/backend/tally/settings.py @@ -36,7 +36,9 @@ def get_required_env(key): SECRET_KEY = get_required_env('SECRET_KEY') # SECURITY WARNING: don't run with debug turned on in production! -DEBUG = get_required_env('DEBUG').lower() == 'true' +_debug_raw = get_required_env('DEBUG') +DEBUG = _debug_raw.lower() == 'true' +print(f"[STARTUP] DEBUG={DEBUG} (raw: {repr(_debug_raw)})") ALLOWED_HOSTS = get_required_env('ALLOWED_HOSTS').split(',') diff --git a/backend/users/views.py b/backend/users/views.py index a687505..0e0a8ef 100644 --- a/backend/users/views.py +++ b/backend/users/views.py @@ -443,8 +443,7 @@ def complete_builder_journey(self, request): Requirements: 1. Has at least one contribution (any type) 2. Has testnet balance > 0 - 3. Has deployed at least one contract on GenLayer - + Also creates Builder profile if it doesn't exist. """ from contributions.models import Contribution, ContributionType @@ -519,30 +518,6 @@ def complete_builder_journey(self, request): # If we can't check balance, we'll allow proceeding (fail open) pass - # Check requirement 3: Has deployed at least one contract - try: - genlayer_service = GenLayerDeploymentService() - - # Convert address to checksum format for GenLayer API - checksum_address = Web3.to_checksum_address(user.address) - - deployment_result = genlayer_service.get_user_deployments(checksum_address) - - if not deployment_result.get('has_deployments', False): - return Response( - {'error': 'You need to deploy at least one contract to complete the builder journey. Use GenLayer Studio to deploy your first contract.'}, - status=status.HTTP_400_BAD_REQUEST - ) - - logger.debug(f"Deployment check passed: {deployment_result.get('deployment_count', 0)} deployments") - - except Exception as e: - logger.error(f"Failed to check deployments: {str(e)}") - return Response( - {'error': 'Failed to verify contract deployments. Please try again later.'}, - status=status.HTTP_500_INTERNAL_SERVER_ERROR - ) - # All requirements met, create the BUILDER contribution and Builder profile atomically try: with transaction.atomic(): diff --git a/frontend/src/components/BuilderProgress.svelte b/frontend/src/components/BuilderProgress.svelte index 9af1050..ea5564e 100644 --- a/frontend/src/components/BuilderProgress.svelte +++ b/frontend/src/components/BuilderProgress.svelte @@ -21,12 +21,12 @@ isRefreshingBalance = false, onCheckRequirements = null, isCheckingRequirements = false, - onCheckDeployments = null, - isCheckingDeployments = false, onOpenStudio = null, onGitHubLinked = null, onCheckRepoStar = null, - isCheckingRepoStar = false + isCheckingRepoStar = false, + onCompleteJourney = null, + isCompletingJourney = false } = $props(); // Network states @@ -181,6 +181,11 @@ // Check if wallet has testnet balance let hasTestnetBalance = $derived(testnetBalance && testnetBalance > 0); + + // Core requirements that gate journey completion + let allCoreRequirementsMet = $derived( + hasBuilderWelcome && !!githubUsername && hasStarredRepo && hasTestnetBalance && hasDeployedContract + );
@@ -584,25 +589,6 @@
{#if showActions}
- {#if !hasDeployedContract && onCheckDeployments} - - {/if} {#if onOpenStudio}
+ + + {#if onCompleteJourney} +
+ +
+ {/if} {/if} \ No newline at end of file diff --git a/frontend/src/lib/api.js b/frontend/src/lib/api.js index f13eb9b..755d03c 100644 --- a/frontend/src/lib/api.js +++ b/frontend/src/lib/api.js @@ -58,8 +58,6 @@ export const usersAPI = { getCurrentUser: () => api.get('/users/me/'), updateUserProfile: (data) => api.patch('/users/me/', data), getAccountBalance: () => api.get('/users/balance/'), - checkDeployments: () => api.get('/users/check_deployments/'), - getDeploymentStatus: () => api.get('/users/deployment_status/'), getActiveValidators: () => api.get('/users/validators/'), getReferrals: () => api.get('/users/referrals/'), getReferralPoints: () => api.get('/users/referral_points/'), diff --git a/frontend/src/routes/BuilderWelcome.svelte b/frontend/src/routes/BuilderWelcome.svelte index 1edba67..cab7d08 100644 --- a/frontend/src/routes/BuilderWelcome.svelte +++ b/frontend/src/routes/BuilderWelcome.svelte @@ -2,9 +2,9 @@ import { onMount } from 'svelte'; import { push } from 'svelte-spa-router'; import { authState } from '../lib/auth'; - import { getCurrentUser, journeyAPI, usersAPI, githubAPI } from '../lib/api'; + import { getCurrentUser, journeyAPI, githubAPI } from '../lib/api'; + import { showError } from '../lib/toastStore'; import { getValidatorBalance } from '../lib/blockchain'; - import Icon from '../components/Icons.svelte'; import BuilderProgress from '../components/BuilderProgress.svelte'; import GitHubLink from '../components/GitHubLink.svelte'; @@ -21,9 +21,6 @@ let error = $state(''); let loading = $state(true); let isRefreshingBalance = $state(false); - let isCheckingDeployments = $state(false); - let hasCheckedDeploymentsOnce = $state(false); - let showStudioInstructions = $state(false); let isCompletingJourney = $state(false); // Debounce utility to prevent button spam @@ -46,16 +43,6 @@ let requirement4Met = $derived(testnetBalance > 0); let requirement5Met = $derived(hasDeployedContract); let allRequirementsMet = $derived(requirement1Met && requirement2Met && requirement3Met && requirement4Met && requirement5Met); - let hasCalledComplete = $state(false); - - // Auto-complete journey when all requirements are met - $effect(() => { - if (allRequirementsMet && !hasCalledComplete && $authState.isAuthenticated && !isCompletingJourney) { - hasCalledComplete = true; - // If we already know deployments exist, just complete immediately - completeBuilderJourney(); - } - }); onMount(async () => { await loadData(); @@ -70,7 +57,6 @@ githubUsername = currentUser?.github_username || ''; // Removed all automatic checks - user must manually refresh each requirement // - checkTestnetBalance() - Removed: only check when user clicks refresh - // - checkDeployments() - Removed: only check when user clicks refresh // - checkRepoStar() - Removed: only check when user clicks refresh } } catch (err) { @@ -92,46 +78,9 @@ } - async function checkDeployments() { - // Show spinner on first check - if (!hasCheckedDeploymentsOnce) { - isCheckingDeployments = true; - } - - try { - const response = await usersAPI.getDeploymentStatus(); - hasDeployedContract = response.data.has_deployments || false; - } catch (err) { - hasDeployedContract = false; - } finally { - hasCheckedDeploymentsOnce = true; - isCheckingDeployments = false; - } - } - - async function refreshDeployments() { - // Manual refresh of deployment status - isCheckingDeployments = true; - try { - await checkDeployments(); - - // If deployments detected and all requirements met, complete immediately - if (hasDeployedContract && requirement1Met && requirement2Met && !hasCalledComplete) { - hasCalledComplete = true; - await completeBuilderJourney(); - } - // Show instructions if still no deployments after manual check - else if (!hasDeployedContract && hasCheckedDeploymentsOnce) { - showStudioInstructions = true; - } - } finally { - isCheckingDeployments = false; - } - } - - function openStudioWithInstructions() { - // Show instructions popup (Studio will be opened from the popup button) - showStudioInstructions = true; + function openStudio() { + hasDeployedContract = true; + window.open('https://studio.genlayer.com', '_blank', 'noopener,noreferrer'); } async function refreshBalance() { @@ -166,11 +115,7 @@ } async function checkRequirements() { - // Check balance and deployments - await Promise.all([ - refreshBalance(), - checkDeployments() - ]); + await refreshBalance(); } async function claimBuilderWelcome() { @@ -203,33 +148,28 @@ } async function completeBuilderJourney() { - if (!$authState.isAuthenticated || !allRequirementsMet) { + if (!$authState.isAuthenticated || !allRequirementsMet || isCompletingJourney) { return; } - // Don't re-check deployments if we already know they exist - // Just proceed with completion isCompletingJourney = true; try { const response = await journeyAPI.completeBuilderJourney(); - // If successful, redirect to profile with success message if (response.status === 201) { - // New builder created sessionStorage.setItem('builderJourneySuccess', 'true'); push(`/participant/${$authState.address}`); } else if (response.status === 200) { - // Already a builder, just redirect push(`/participant/${$authState.address}`); } } catch (err) { - // If already has the contribution and Builder profile, redirect anyway if (err.response?.status === 200) { push(`/participant/${$authState.address}`); + } else if (err.response?.status === 400) { + showError(err.response?.data?.error || 'Some requirements are not yet met. Please check and try again.'); } else { - // Reset flag to allow retry - hasCalledComplete = false; + showError('Something went wrong. Please try again later.'); } } finally { isCompletingJourney = false; @@ -238,8 +178,8 @@ // Create debounced versions AFTER all functions are defined (500ms delay) const debouncedRefreshBalance = debounce(refreshBalance, 500); - const debouncedRefreshDeployments = debounce(refreshDeployments, 500); const debouncedCheckRepoStar = debounce(checkRepoStar, 500); + const debouncedCompleteJourney = debounce(completeBuilderJourney, 500); @@ -405,102 +345,14 @@ isRefreshingBalance={isRefreshingBalance} onCheckRequirements={checkRequirements} isCheckingRequirements={false} - onCheckDeployments={debouncedRefreshDeployments} - isCheckingDeployments={isCheckingDeployments} - onOpenStudio={openStudioWithInstructions} + onOpenStudio={openStudio} onGitHubLinked={handleGitHubLinked} onCheckRepoStar={debouncedCheckRepoStar} isCheckingRepoStar={isCheckingRepoStar} + onCompleteJourney={debouncedCompleteJourney} + isCompletingJourney={isCompletingJourney} /> - - {#if isCompletingJourney} -
-
- - - - - Completing your Builder Journey... -
-
- {:else if hasBuilderWelcome || (allRequirementsMet && hasCalledComplete)} -
- -
- {/if} - - - {#if showStudioInstructions} -
showStudioInstructions = false}> -
e.stopPropagation()}> -
-
-

- Connect Your Wallet in Studio -

- -
- -
-

- To deploy contracts on GenLayer Studio, you need to: -

- -
    -
  1. - Connect your wallet in GenLayer Studio using the same address: - {$authState.address} -
  2. -
  3. - Deploy your contract using the Studio interface -
  4. -
  5. - Return here and click refresh to verify your deployment -
  6. -
- -
-

- Important: Make sure to use the same wallet address in Studio that you're using here. -

-
-
- -
- - -
-
-
-
- {/if} \ No newline at end of file diff --git a/frontend/src/routes/Profile.svelte b/frontend/src/routes/Profile.svelte index e5651d4..2514925 100644 --- a/frontend/src/routes/Profile.svelte +++ b/frontend/src/routes/Profile.svelte @@ -15,7 +15,7 @@ import { authState } from '../lib/auth'; import { getValidatorBalance } from '../lib/blockchain'; import Avatar from '../components/Avatar.svelte'; - import { showSuccess, showWarning } from '../lib/toastStore'; + import { showSuccess, showWarning, showError } from '../lib/toastStore'; import { parseMarkdown } from '../lib/markdownLoader.js'; // Import route params from svelte-spa-router @@ -52,11 +52,10 @@ let hasDeployedContract = $state(false); let isRefreshingBalance = $state(false); let isClaimingBuilderBadge = $state(false); - let hasCalledComplete = $state(false); let hasStarredRepo = $state(false); let repoToStar = $state('genlayerlabs/genlayer-project-boilerplate'); let isCheckingRepoStar = $state(false); - let isCheckingDeployments = $state(false); + let isCompletingJourney = $state(false); let referralData = $state(null); let loadingReferrals = $state(false); let hasShownStatsErrorToast = $state(false); @@ -70,19 +69,6 @@ $authState.address?.toLowerCase() === participant.address.toLowerCase() ); - // Derived states for builder requirements - let requirement1Met = $derived(participant?.has_builder_welcome || false); - let requirement2Met = $derived(testnetBalance > 0); - let allRequirementsMet = $derived(requirement1Met && requirement2Met); - - // Auto-complete journey when all requirements are met - $effect(() => { - if (allRequirementsMet && !hasCalledComplete && isOwnProfile && !participant?.builder) { - hasCalledComplete = true; - completeBuilderJourney(); - } - }); - // Determine participant type let participantType = $derived( !participant ? null : @@ -301,35 +287,6 @@ } } - async function completeBuilderJourney() { - if (!$authState.isAuthenticated || !allRequirementsMet) { - return; - } - - try { - const response = await journeyAPI.completeBuilderJourney(); - - // If successful, show success notification and reload data - if (response.status === 201 || response.status === 200) { - showSuccess('Congratulations! 🎉 You are now a GenLayer Builder! Your Builder profile has been created and you can start contributing to the ecosystem.'); - - // Reload participant data to get Builder profile - const updatedUser = await getCurrentUser(); - participant = updatedUser; - } - } catch (err) { - // If already has the contribution and Builder profile - if (err.response?.status === 200) { - // Still reload data - const updatedUser = await getCurrentUser(); - participant = updatedUser; - } else { - // Reset flag to allow retry - hasCalledComplete = false; - } - } - } - async function handleGitHubLinked(updatedUser) { // Update participant with the updated user info from GitHubLink component participant = updatedUser; @@ -350,22 +307,34 @@ } } - async function checkDeployments() { - isCheckingDeployments = true; + function openStudio() { + hasDeployedContract = true; + window.open('https://studio.genlayer.com', '_blank', 'noopener,noreferrer'); + } + + async function completeBuilderJourney() { + if (!$authState.isAuthenticated || isCompletingJourney) return; + + isCompletingJourney = true; try { - const response = await usersAPI.getDeploymentStatus(); - hasDeployedContract = response.data.has_deployments || false; + const response = await journeyAPI.completeBuilderJourney(); + if (response.status === 201 || response.status === 200) { + // Reload participant data to reflect builder status + await fetchParticipantData($authState.address); + } } catch (err) { - hasDeployedContract = false; + if (err.response?.status === 200) { + await fetchParticipantData($authState.address); + } else if (err.response?.status === 400) { + showError(err.response?.data?.error || 'Some requirements are not yet met.'); + } else { + showError('Something went wrong. Please try again later.'); + } } finally { - isCheckingDeployments = false; + isCompletingJourney = false; } } - function openStudio() { - window.open('https://studio.genlayer.com', '_blank', 'noopener,noreferrer'); - } - async function fetchParticipantData(participantAddress) { try { loading = true; @@ -460,12 +429,6 @@ testnetBalance = 0; }); - // Check for contract deployments asynchronously - usersAPI.getDeploymentStatus().then(deploymentResult => { - hasDeployedContract = deploymentResult.data.has_deployments || false; - }).catch(err => { - hasDeployedContract = false; - }); } } } catch (err) { @@ -1347,9 +1310,9 @@ onGitHubLinked={handleGitHubLinked} onCheckRepoStar={checkRepoStar} isCheckingRepoStar={isCheckingRepoStar} - onCheckDeployments={checkDeployments} - isCheckingDeployments={isCheckingDeployments} onOpenStudio={openStudio} + onCompleteJourney={completeBuilderJourney} + isCompletingJourney={isCompletingJourney} /> {:else}