Creating & Accessing User Wallets
Sphere revolutionizes user onboarding in Web3 by enabling the programatic creation of wallets that users can access with familiar credentials like an email address or phone number. This process eliminates the traditional complexities of seed phrases for the end-user, relying on a secure One-Time Password (OTP) flow for initial wallet access and authentication.
This page guides you through the complete flow: from user signup and OTP verification to the initial authenticated session that makes the user's smart wallet operational.
The Wallet Creation & Initial Authentication Flow
Accessing a Sphere smart wallet for the first time (which often includes its on-chain creation if it doesn't exist yet) is a straightforward process using one of three authentication methods. Here's a breakdown:
⚠️ CRITICAL: Authentication Parameter Rules
Authentication parameters are MUTUALLY EXCLUSIVE:
- ❌ NEVER provide
phoneNumber
+- ❌ NEVER provide
phoneNumber
+externalId
together- ❌ NEVER provide
externalId
together- ❌ NEVER provide all three together
Choose ONE authentication method per request:
- 📱 Phone-based: Use
phoneNumber
+otpCode
ONLY- 📧 Email-based: Use
otpCode
ONLY- 🔑 External ID: Use
externalId
+password
ONLY
Authentication Method 1: Phone + OTP
- User Identification (
signup
): The user provides their phone number. Your application usessphere.auth.signup()
with phone number ONLY to register this identifier. - Send OTP (
sendOtp
): Your application callssphere.auth.sendOtp()
to request an OTP be sent to the user's phone. - User Verification: The user receives the OTP and provides it back to your application.
- Login & Wallet Activation (
login
): Your application callssphere.auth.login()
with phone number and OTP ONLY.
Authentication Method 2: Email + OTP
- User Identification (
signup
): The user provides their email address. Your application usessphere.auth.signup()
with email ONLY to register this identifier. - Send OTP (
sendOtp
): Your application callssphere.auth.sendOtp()
to request an OTP be sent to the user's email. - User Verification: The user receives the OTP and provides it back to your application.
- Login & Wallet Activation (
login
): Your application callssphere.auth.login()
with email and OTP ONLY.
Authentication Method 3: External ID + Password
- User Identification (
signup
): The user provides their external ID (username/email) and password. Your application usessphere.auth.signup()
with external ID and password ONLY. - Login & Wallet Activation (
login
): Your application callssphere.auth.login()
with external ID and password ONLY (no OTP required).
Common Final Steps (All Methods):
- The Stratosphere Network validates the credentials.
- If valid, the associated smart contract wallet address is determined and the login method returns a response containing the
accessToken
, user ID, and wallet addresses. - The SDK automatically sets the bearer token when present in the login response, enabling authenticated SDK operations for that user.
Diagram: Wallet Creation and Initial Authentication Flow
Code Examples: Full Onboarding Journey
This TypeScript example demonstrates the end-to-end process for all three authentication methods:
import Sphere, {
Environment,
LoginResponse,
} from "@stratosphere-network/wallet"; // Adjust import path
// Initialize the Sphere SDK
const sphere = new Sphere({
environment: Environment.DEVELOPMENT, // Or Environment.PRODUCTION
apiKey: "YOUR_PROJECT_API_KEY", // Replace with your actual API key
});
// Example 1: Phone + OTP Authentication
async function onboardUserWithPhone(phoneNumber: string) {
console.log(`Starting phone onboarding for: ${phoneNumber}`);
try {
// Step 1: Sign up with phone number ONLY
// ⚠️ Do NOT provide email or externalId when using phoneNumber
const signupResponse = await sphere.auth.signup({
phoneNumber: phoneNumber,
// email: "user@example.com", // ❌ NEVER provide with phoneNumber
// externalId: "user123", // ❌ NEVER provide with phoneNumber
});
console.log("Phone signup successful:", signupResponse.message);
// Step 2: Send OTP to phone ONLY
// ⚠️ Do NOT provide email when using phone
await sphere.auth.sendOtp({
phone: phoneNumber,
// email: "user@example.com", // ❌ NEVER provide with phone
});
console.log(`OTP sent to: ${phoneNumber}`);
// Step 3: Simulate user providing the OTP
// In a real application, you would get this from user input
const otpCodeFromUser = "123456"; // Replace with actual OTP input mechanism
console.log(`User provided OTP: ${otpCodeFromUser}`);
// Step 4: Login with phone + OTP ONLY
// ⚠️ Do NOT provide email or externalId when using phoneNumber
const loginResponse: LoginResponse = await sphere.auth.login({
phoneNumber: phoneNumber,
otpCode: otpCodeFromUser,
// email: "user@example.com", // ❌ NEVER provide with phoneNumber
// externalId: "user123", // ❌ NEVER provide with phoneNumber
});
console.log("Phone login successful!");
console.log("User ID:", loginResponse.user);
console.log("Wallet Address:", loginResponse.address);
console.log("BTC Address:", loginResponse.btcAddress);
// SDK automatically sets bearer token
const userDetails = await sphere.auth.getUser();
console.log("Fetched user details:", userDetails.user);
return {
walletAddress: loginResponse.address,
btcAddress: loginResponse.btcAddress,
accessToken: loginResponse.accessToken,
userId: loginResponse.user,
};
} catch (error) {
console.error("Phone onboarding error:", error);
throw error;
}
}
// Example 2: Email + OTP Authentication
async function onboardUserWithEmail(email: string) {
console.log(`Starting email onboarding for: ${email}`);
try {
// Step 1: Sign up with email ONLY
// ⚠️ Do NOT provide phoneNumber or externalId when using email
const signupResponse = await sphere.auth.signup({
email: email,
// phoneNumber: "+1234567890", // ❌ NEVER provide with email
// externalId: "user123", // ❌ NEVER provide with email
});
console.log("Email signup successful:", signupResponse.message);
// Step 2: Send OTP to email ONLY
// ⚠️ Do NOT provide phone when using email
await sphere.auth.sendOtp({
email: email,
// phone: "+1234567890", // ❌ NEVER provide with email
});
console.log(`OTP sent to: ${email}`);
// Step 3: Simulate user providing the OTP
const otpCodeFromUser = "123456"; // Replace with actual OTP input mechanism
console.log(`User provided OTP: ${otpCodeFromUser}`);
// Step 4: Login with email + OTP ONLY
// ⚠️ Do NOT provide phoneNumber or externalId when using email
const loginResponse: LoginResponse = await sphere.auth.login({
email: email,
otpCode: otpCodeFromUser,
// phoneNumber: "+1234567890", // ❌ NEVER provide with email
// externalId: "user123", // ❌ NEVER provide with email
});
console.log("Email login successful!");
console.log("User ID:", loginResponse.user);
console.log("Wallet Address:", loginResponse.address);
console.log("BTC Address:", loginResponse.btcAddress);
// SDK automatically sets bearer token
const userDetails = await sphere.auth.getUser();
console.log("Fetched user details:", userDetails.user);
return {
walletAddress: loginResponse.address,
btcAddress: loginResponse.btcAddress,
accessToken: loginResponse.accessToken,
userId: loginResponse.user,
};
} catch (error) {
console.error("Email onboarding error:", error);
throw error;
}
}
// Example 3: External ID + Password Authentication
async function onboardUserWithExternalId(externalId: string, password: string) {
console.log(`Starting external ID onboarding for: ${externalId}`);
try {
// Step 1: Sign up with externalId + password ONLY
// ⚠️ Do NOT provide phoneNumber or email when using externalId
const signupResponse = await sphere.auth.signup({
externalId: externalId,
password: password,
// phoneNumber: "+1234567890", // ❌ NEVER provide with externalId
// email: "user@example.com", // ❌ NEVER provide with externalId
});
console.log("External ID signup successful:", signupResponse.message);
// Step 2: Login with externalId + password ONLY (no OTP required)
// ⚠️ Do NOT provide phoneNumber or email when using externalId
const loginResponse: LoginResponse = await sphere.auth.login({
externalId: externalId,
password: password,
// phoneNumber: "+1234567890", // ❌ NEVER provide with externalId
// email: "user@example.com", // ❌ NEVER provide with externalId
});
console.log("External ID login successful!");
console.log("User ID:", loginResponse.user);
console.log("Wallet Address:", loginResponse.address);
console.log("BTC Address:", loginResponse.btcAddress);
// SDK automatically sets bearer token
const userDetails = await sphere.auth.getUser();
console.log("Fetched user details:", userDetails.user);
return {
walletAddress: loginResponse.address,
btcAddress: loginResponse.btcAddress,
accessToken: loginResponse.accessToken,
userId: loginResponse.user,
};
} catch (error) {
console.error("External ID onboarding error:", error);
throw error;
}
}
// --- Example Usage ---
/*
// Phone authentication
onboardUserWithPhone("+12345678900")
.then(result => {
console.log("Phone onboarding successful:", result);
})
.catch(error => {
console.error("Phone onboarding failed:", error.message);
});
// Email authentication
onboardUserWithEmail("user@example.com")
.then(result => {
console.log("Email onboarding successful:", result);
})
.catch(error => {
console.error("Email onboarding failed:", error.message);
});
// External ID authentication
onboardUserWithExternalId("user@example.com", "secure-password")
.then(result => {
console.log("External ID onboarding successful:", result);
})
.catch(error => {
console.error("External ID onboarding failed:", error.message);
});
*/
Key Concepts & Intuition
Understanding these concepts will help you grasp how Sphere wallets work:
- Deterministic Wallet Addresses: Sphere smart wallet addresses can often be pre-calculated (derived deterministically) based on the user's unique identifier. The login response confirms the address associated with the authenticated user.
- User-Centric Authentication: Instead of burdening users with seed phrases, Sphere leverages familiar OTPs for initial access and authentication. The security of the wallet is tied to the user's control over their email/phone and the OTP process, combined with the underlying smart contract security.
- Role of Stratosphere Network: The Stratosphere Network is crucial. It manages user registrations (
signup
), orchestrates OTP generation and verification (sendOtp
,login
), and upon successful authentication, returns the necessary data (access token and user/wallet details) that enable your application to interact with the user's wallet. - Direct Response Flow: The authentication flow is synchronous - the
login
method directly returns all necessary information, making integration straightforward without the need for event handling.
Important Considerations
- Error Handling: The example above includes basic error handling. Robust applications should implement comprehensive error checking and user feedback for each step (signup, OTP sending, login, event handling).
- OTP Input: Replace the hardcoded
otpCodeFromUser
with a secure method of obtaining the OTP from your user interface. - User Identifiers: Decide on the primary identifier (email, phone, or both) for your application's onboarding flow. Ensure your
signup
,sendOtp
, andlogin
calls are consistent.
Next Steps
Once a user's wallet is created and you have an authenticated SDK instance (see the "Wallet Creation & Initial Authentication Flow" and "Code Example" sections above for details on obtaining the accessToken
and setting the bearer token):
- If building a flow for returning users who need to log in again, see Verify OTP for Subsequent Logins & Manage Sessions.
- Proceed to Get Wallet Balances to query the user's assets.
- Explore how to Make Transactions (this link is an example; please update if you have a dedicated transactions page).
For further assistance, refer to the full SDK documentation or join the Sphere community.