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:
-
-
-
- -
- Connect your wallet in GenLayer Studio using the same address:
-
{$authState.address}
-
- -
- Deploy your contract using the Studio interface
-
- -
- Return here and click refresh to verify your deployment
-
-
-
-
-
- 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}