Skip to main content

Sphere SDK Quickstart

Welcome to the Sphere SDK! This guide will help you get up and running quickly, enabling you to integrate Sphere's powerful features into your applications.

Sphere provides a comprehensive suite of tools for managing digital assets, payments, and more. While this quickstart focuses on our TypeScript SDK, we're excited to announce that SDKs for Python, Go, and Rust are coming soon.

Table of Contents

Project Setup

Before installing and using the Sphere SDK, you must first create and configure a project on the Sphere Dashboard.

Step 1: Create Your Sphere Project

  1. Visit the Dashboard: Navigate to https://dashboard.sphere-id.com
  2. Sign Up/Login: Create an account or sign in
  3. Create New Project:
    • Click "Create Project"
    • Enter project name and description
    • Select environment (Development for testing, Production for live apps)

Step 2: Configure Allowed Origins (Critical for CORS)

⚠️ This step is mandatory - without proper origin configuration, your application will be blocked by CORS errors.

  1. Navigate to Project Settings in your dashboard
  2. Add Allowed Origins for all domains that will access your application:
# Development
http://localhost:3000
http://localhost:3001
http://127.0.0.1:3000

# Staging
https://staging.yourapp.com

# Production
https://yourapp.com
https://www.yourapp.com

# Mobile (if applicable)
yourapp://
com.yourcompany.yourapp://

Important: Any request from an origin not in this list will be rejected. Always add your development, staging, and production domains.

Step 3: Get Your API Key

  1. Find API Keys Section in your project dashboard
  2. Copy the Project API Key - you'll need this for SDK initialization
  3. Store Securely - treat this like a password and never expose it in client-side code in production

If you plan to use Payment Links, you must deploy a payment widget to handle cross-platform link sharing.

Why You Need a Widget

Payment links created in your app need to work across different platforms (web, mobile, Telegram). Since there's no universal link format, Sphere uses a widget-based approach:

Flow: Payment Link → Your Widget → Platform Selection → Target App

Deploy the Widget

  1. Clone the Template:

    git clone https://github.com/stratosphere-network/sphere-widget.git
    cd sphere-widget
  2. Configure with Your API Key:

    // Configure the widget with your project API key
    const config = {
    projectApiKey: "your-project-api-key-from-dashboard",
    supportedPlatforms: ["web", "mobile", "telegram"],
    // Add your branding and styling
    };
  3. Deploy to Public URL:

    • Deploy to services like Vercel, Netlify, or your own hosting
    • Example URL: https://widget.yourapp.com
    • Ensure the widget URL is publicly accessible
  4. Register Widget URL:

    • In your Sphere dashboard, register your deployed widget URL
    • This tells Sphere where to redirect payment link recipients

Widget Configuration Example

// widget/config.js
export const widgetConfig = {
// Your project API key from dashboard
projectApiKey: process.env.SPHERE_PROJECT_API_KEY,

// Platforms your app supports
supportedPlatforms: ["web", "mobile", "telegram"],

// Platform-specific redirect URLs (registered separately in your app)
platforms: {
web: {
name: "Web App",
icon: "/icons/web.svg",
},
mobile: {
name: "Mobile App",
icon: "/icons/mobile.svg",
},
telegram: {
name: "Telegram Bot",
icon: "/icons/telegram.svg",
},
},

// Your branding
branding: {
appName: "Your App Name",
logo: "/logo.png",
primaryColor: "#007bff",
},
};

The widget will:

  1. Receive payment link clicks
  2. Use your API key to fetch registered redirect URLs for each platform
  3. Show users platform options
  4. Redirect to the appropriate platform-specific app

Installation

Install the Sphere SDK using your favorite package manager:

npm install @stratosphere-network/wallet@latest
# or
yarn add @stratosphere-network/wallet@latest

Initialization

To start using the SDK, import the Sphere client and initialize it with your configuration using the API key from your project setup.

import Sphere, { Environment } from "@stratosphere-network/wallet"; // Adjust import path if needed

const sphere = new Sphere({
environment: Environment.DEVELOPMENT, // Or Environment.PRODUCTION
apiKey: "YOUR_PROJECT_API_KEY", // From your Sphere Dashboard project
});

console.log("Sphere SDK Initialized!");
console.log("Current Environment:", sphere.getEnvironment());
console.log("Base URL:", sphere.getBaseUrl());

⚠️ Important: Use the project API key you obtained from the Sphere Dashboard during project setup. Ensure your application's domain is added to the allowed origins in your project settings to avoid CORS errors.

Authentication

Sphere SDK supports two main types of authentication: API Key authentication and Bearer Token authentication for user-specific actions.

API Key Authentication

This is required to interact with all the functions. You get it from https://dashboard.sphere-id.com.

User Authentication

For actions performed on behalf of a user (e.g., managing their wallet, making transactions), user authentication is required. Sphere supports multiple authentication methods with MUTUALLY EXCLUSIVE parameters.

⚠️ CRITICAL: Authentication Parameter Rules

Authentication parameters are MUTUALLY EXCLUSIVE:

  • NEVER provide phoneNumber + email together
  • NEVER provide phoneNumber + externalId together
  • NEVER provide email + externalId together
  • NEVER provide all three together

Choose ONE authentication method per request:

  • 📱 Phone-based: Use phoneNumber + otpCode ONLY
  • 📧 Email-based: Use email + otpCode ONLY
  • 🔑 External ID: Use externalId + password ONLY

Authentication Methods:

  1. Phone + OTP Authentication: User registers and logs in using phone number with OTP verification
  2. Email + OTP Authentication: User registers and logs in using email with OTP verification
  3. External ID + Password Authentication: User registers and logs in using external ID (username/email) with password

The Authentication Flow:

The process varies by authentication method:

Phone/Email + OTP Flow: a. Initialize the Sphere SDK b. Signup user with sphere.auth.signup() (phone OR email only) c. Send OTP using sphere.auth.sendOtp() (phone OR email only) d. User provides the received OTP e. Login with sphere.auth.login() (phone+OTP OR email+OTP only) f. SDK automatically sets bearer token from the response

External ID + Password Flow: a. Initialize the Sphere SDK b. Signup user with sphere.auth.signup() (externalId + password only) c. Login with sphere.auth.login() (externalId + password only) d. SDK automatically sets bearer token from the response

Code Examples:

// Example 1: Phone + OTP Authentication
async function phoneAuthFlow() {
const userPhoneNumber = "+12345678900"; // E.164 format

try {
// Step 1: Signup with phone number ONLY
// ⚠️ Do NOT provide email or externalId when using phoneNumber
const signupResponse = await sphere.auth.signup({
phoneNumber: userPhoneNumber,
// email: "user@example.com", // ❌ NEVER provide with phoneNumber
// externalId: "user123", // ❌ NEVER provide with phoneNumber
});
console.log("Signup successful:", signupResponse.message);

// Step 2: Send OTP to phone ONLY
// ⚠️ Do NOT provide email when using phone
const otpResponse = await sphere.auth.sendOtp({
phone: userPhoneNumber,
// email: "user@example.com", // ❌ NEVER provide with phone
});
console.log("OTP sent:", otpResponse.message);

// --- User enters OTP received ---
const otpCodeFromUser = "123456"; // Replace with actual OTP from user input

// Step 3: Login with phone + OTP ONLY
// ⚠️ Do NOT provide email or externalId when using phoneNumber
const loginResponse = await sphere.auth.login({
phoneNumber: userPhoneNumber,
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 the bearer token
await fetchUserDetails();
} catch (error) {
console.error("Phone authentication error:", error);
}
}

// Example 2: Email + OTP Authentication
async function emailAuthFlow() {
const userEmail = "user@example.com";

try {
// Step 1: Signup with email ONLY
// ⚠️ Do NOT provide phoneNumber or externalId when using email
const signupResponse = await sphere.auth.signup({
email: userEmail,
// phoneNumber: "+1234567890", // ❌ NEVER provide with email
// externalId: "user123", // ❌ NEVER provide with email
});
console.log("Signup successful:", signupResponse.message);

// Step 2: Send OTP to email ONLY
// ⚠️ Do NOT provide phone when using email
const otpResponse = await sphere.auth.sendOtp({
email: userEmail,
// phone: "+1234567890", // ❌ NEVER provide with email
});
console.log("OTP sent:", otpResponse.message);

// --- User enters OTP received ---
const otpCodeFromUser = "123456"; // Replace with actual OTP from user input

// Step 3: Login with email + OTP ONLY
// ⚠️ Do NOT provide phoneNumber or externalId when using email
const loginResponse = await sphere.auth.login({
email: userEmail,
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 the bearer token
await fetchUserDetails();
} catch (error) {
console.error("Email authentication error:", error);
}
}

// Example 3: External ID + Password Authentication
async function externalIdAuthFlow() {
const externalId = "user@example.com"; // Can be email, username, or any unique ID
const password = "secure-password";

try {
// Step 1: Signup 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("Signup successful:", signupResponse.message);

// Step 2: Login with externalId + password ONLY
// ⚠️ Do NOT provide phoneNumber or email when using externalId
const 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 the bearer token
await fetchUserDetails();
} catch (error) {
console.error("External ID authentication error:", error);
}
}

// Common function for all authentication methods
async function fetchUserDetails() {
if (sphere.isAuthenticated()) {
console.log("User is authenticated!");
try {
const userDetails = await sphere.auth.getUser();
console.log("User Details:", userDetails.user);
} catch (error) {
console.error("Error fetching user details:", error);
}
} else {
console.log("User is not authenticated.");
}
}

// Usage examples:
// phoneAuthFlow(); // For phone-based authentication
// emailAuthFlow(); // For email-based authentication
// externalIdAuthFlow(); // For external ID + password authentication

3. Setting Bearer Token Manually (For Returning Users):

If you've saved the accessToken from a previous session, you can restore the authenticated state:

const accessTokenFromLogin = "THE_ACCESS_TOKEN_FROM_LOGIN_RESPONSE";
sphere.setBearerToken(accessTokenFromLogin);

if (sphere.auth.isAuthenticated()) {
console.log("User is now authenticated with the saved token.");
}

4. OTP Verification (Optional):

You can verify OTP codes without logging in using the verifyOtp method:

// Verify OTP for phone ONLY
// ⚠️ Do NOT provide email when using phone
const phoneVerification = await sphere.auth.verifyOtp({
phone: "+1234567890",
code: "123456",
// email: "user@example.com", // ❌ NEVER provide with phone
});

// Verify OTP for email ONLY
// ⚠️ Do NOT provide phone when using email
const emailVerification = await sphere.auth.verifyOtp({
email: "user@example.com",
code: "123456",
// phone: "+1234567890", // ❌ NEVER provide with email
});

console.log("OTP verification:", phoneVerification.status);

5. User Account Management:

// Get current user information
if (sphere.auth.isAuthenticated()) {
const userInfo = await sphere.auth.getUser();
console.log("Current user:", userInfo.user);
}

// Delete user account (requires authentication)
const deleteResponse = await sphere.auth.deleteUser({
// Add required parameters for user deletion
confirmDeletion: true, // Example parameter
});
console.log("Account deleted:", deleteResponse.message);
// Note: SDK automatically clears bearer token after deletion

6. Logging Out:

To log a user out and clear their session:

// Simple logout - clears bearer token
sphere.auth.logout();

// Check authentication status
if (!sphere.auth.isAuthenticated()) {
console.log("User has been logged out.");
}

// Optional: Clear any locally stored tokens
localStorage.removeItem("sphereAccessToken");

User Management

Once a user is authenticated, you can access user information and manage their account.

Getting User Information

Retrieve the current user's profile information:

// Get current user information
async function getUserInfo() {
if (!sphere.auth.isAuthenticated()) {
console.log("User must be authenticated to get user information.");
return;
}

try {
const userResponse = await sphere.auth.getUser();
console.log("User Information:", userResponse.user);
console.log("User ID:", userResponse.user.id);
console.log("External ID:", userResponse.user.externalId);
console.log("Email:", userResponse.user.email);
console.log("Phone Number:", userResponse.user.phoneNumber);
console.log("Address:", userResponse.user.address);
console.log("Created At:", userResponse.user.createdAt);
console.log("Updated At:", userResponse.user.updatedAt);
console.log("Referrer:", userResponse.user.referrer);
console.log("Telegram ID:", userResponse.user.telegramId);
} catch (error) {
console.error("Error fetching user information:", error);
}
}

// Usage
getUserInfo();

Deleting User Account

Delete a user account using the same mutually exclusive authentication parameters as login:

⚠️ CRITICAL: Delete User Parameter Rules

Authentication parameters are MUTUALLY EXCLUSIVE for user deletion:

  • NEVER provide phoneNumber + email together
  • NEVER provide phoneNumber + externalId together
  • NEVER provide email + externalId together
  • NEVER provide all three together

Choose ONE authentication method per request:

  • 📱 Phone-based: Use phoneNumber + otpCode ONLY
  • 📧 Email-based: Use email + otpCode ONLY
  • 🔑 External ID: Use externalId + password ONLY

Delete User Examples:

// Example 1: Delete with External ID + Password ONLY
async function deleteUserWithExternalId() {
if (!sphere.auth.isAuthenticated()) {
console.log("User must be authenticated to delete account.");
return;
}

try {
// ⚠️ Do NOT provide phoneNumber or email when using externalId
const deleteResponse = await sphere.auth.deleteUser({
externalId: "user@example.com",
password: "secure-password",
// phoneNumber: "+1234567890", // ❌ NEVER provide with externalId
// email: "user@example.com", // ❌ NEVER provide with externalId
// otpCode: "123456", // ❌ NEVER provide with externalId
});

console.log("User deleted successfully:", deleteResponse.message);
console.log("User is now logged out");

// Note: SDK automatically clears bearer token after deletion
if (!sphere.auth.isAuthenticated()) {
console.log("✅ Bearer token cleared automatically");
}
} catch (error) {
console.error("Error deleting user with external ID:", error);
}
}

// Example 2: Delete with Phone + OTP ONLY
async function deleteUserWithPhone() {
if (!sphere.auth.isAuthenticated()) {
console.log("User must be authenticated to delete account.");
return;
}

try {
const phoneNumber = "+1234567890";

// Step 1: Send OTP to phone
// ⚠️ 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 phone for account deletion");

// --- User enters OTP received ---
const otpCodeFromUser = "123456"; // Replace with actual OTP from user input

// Step 2: Delete with phone + OTP ONLY
// ⚠️ Do NOT provide email or externalId when using phoneNumber
const deleteResponse = await sphere.auth.deleteUser({
phoneNumber: phoneNumber,
otpCode: otpCodeFromUser,
// email: "user@example.com", // ❌ NEVER provide with phoneNumber
// externalId: "user123", // ❌ NEVER provide with phoneNumber
// password: "password", // ❌ NEVER provide with phoneNumber
});

console.log("User deleted successfully:", deleteResponse.message);
console.log("User is now logged out");

// Note: SDK automatically clears bearer token after deletion
if (!sphere.auth.isAuthenticated()) {
console.log("✅ Bearer token cleared automatically");
}
} catch (error) {
console.error("Error deleting user with phone:", error);
}
}

// Example 3: Delete with Email + OTP ONLY
async function deleteUserWithEmail() {
if (!sphere.auth.isAuthenticated()) {
console.log("User must be authenticated to delete account.");
return;
}

try {
const email = "user@example.com";

// Step 1: Send OTP to email
// ⚠️ 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 for account deletion");

// --- User enters OTP received ---
const otpCodeFromUser = "123456"; // Replace with actual OTP from user input

// Step 2: Delete with email + OTP ONLY
// ⚠️ Do NOT provide phoneNumber or externalId when using email
const deleteResponse = await sphere.auth.deleteUser({
email: email,
otpCode: otpCodeFromUser,
// phoneNumber: "+1234567890", // ❌ NEVER provide with email
// externalId: "user123", // ❌ NEVER provide with email
// password: "password", // ❌ NEVER provide with email
});

console.log("User deleted successfully:", deleteResponse.message);
console.log("User is now logged out");

// Note: SDK automatically clears bearer token after deletion
if (!sphere.auth.isAuthenticated()) {
console.log("✅ Bearer token cleared automatically");
}
} catch (error) {
console.error("Error deleting user with email:", error);
}
}

// Usage examples:
// deleteUserWithExternalId(); // For external ID + password deletion
// deleteUserWithPhone(); // For phone + OTP deletion
// deleteUserWithEmail(); // For email + OTP deletion

Complete User Management Example:

// Complete user management workflow
async function userManagementWorkflow() {
try {
// Check authentication status
if (sphere.auth.isAuthenticated()) {
console.log("✅ User is authenticated");

// Get user details
const userResponse = await sphere.auth.getUser();
console.log("👤 User info:", userResponse.user);

// Example: Display user profile in your app
const user = userResponse.user;
console.log(`
Profile Information:
- ID: ${user.id}
- External ID: ${user.externalId}
- Email: ${user.email || "Not provided"}
- Phone: ${user.phoneNumber || "Not provided"}
- Address: ${user.address || "Not provided"}
- Created: ${user.createdAt}
- Updated: ${user.updatedAt}
- Referrer: ${user.referrer || "None"}
- Telegram ID: ${user.telegramId || "Not linked"}
`);

// User can choose to delete account if needed
// Note: In a real app, you'd have UI buttons/forms for this
// deleteUserWithExternalId(); // Uncomment to test deletion
} else {
console.log("❌ User is not authenticated");
console.log("Please authenticate first using one of the auth methods");
}
} catch (error) {
console.error("Error in user management workflow:", error);
}
}

// Usage
userManagementWorkflow();

Important Notes:

  • Authentication Required: All user management operations require the user to be authenticated
  • Automatic Token Clearing: The deleteUser() method automatically clears the bearer token from the SDK
  • Same Auth Rules Apply: User deletion follows the same mutually exclusive parameter rules as login
  • Irreversible Action: User deletion is permanent and cannot be undone
  • Data Cleanup: Deleting a user will remove all associated data including wallets, transactions, and payment links

Core Functionalities & Use Cases

Here are some examples of how to use the Sphere SDK for common tasks. Ensure the user is authenticated for user-specific actions.

Managing Wallets

Retrieve token and chain balances for the authenticated user.

import { TokenSymbol, ChainName } from "@stratosphere-network/wallet"; // Adjust import path if needed

async function walletExamples() {
if (!sphere.isAuthenticated()) {
console.log("Please authenticate the user first.");
return;
}

try {
// Get balance for a specific token (e.g., USDC on Ethereum)
const usdcBalance = await sphere.wallet.getTokenBalance({
token: TokenSymbol.USDC,
chain: ChainName.ETHEREUM,
});
console.log("USDC on Ethereum Balance:", usdcBalance.data);

// Get balance for a specific token across all supported chains
const ethBalanceAcrossChains = await sphere.wallet.getTokenBalance({
token: TokenSymbol.ETH,
});
console.log("ETH Balance Across Chains:", ethBalanceAcrossChains.data);

// Get all chain balances for the user (e.g., native assets on all chains)
const allChainBalances = await sphere.wallet.getChainBalance();
console.log("All Chain Balances:", allChainBalances.data);

// Get balance for a specific chain
const polygonBalances = await sphere.wallet.getChainBalance({
chain: ChainName.POLYGON,
});
console.log("Polygon Balances:", polygonBalances.data);
} catch (error) {
console.error("Wallet Error:", error);
}
}

// Make sure user is authenticated before calling
// userAuthExample().then(walletExamples);

Making Transactions

Send tokens to another address.

import { TokenSymbol, ChainName } from "@stratosphere-network/wallet";

async function transactionExamples() {
if (!sphere.isAuthenticated()) {
console.log("Please authenticate the user first.");
return;
}

try {
const transactionDetails = {
to: "RECEIVER_WALLET_ADDRESS",
value: "0.01", // Amount as a string
token: TokenSymbol.USDC,
chain: ChainName.POLYGON,
// type: 'gasless' // Optional: for gasless transactions
};

const txResponse = await sphere.transactions.send(transactionDetails);
console.log("Transaction Sent:", txResponse);

// Get transaction history
const history = await sphere.transactions.getHistory({
limit: 10,
page: 1,
});
console.log("Transaction History:", history);
} catch (error) {
console.error("Transaction Error:", error);
}
}

// Make sure user is authenticated before calling
// userAuthExample().then(transactionExamples);

The PaymentLinksService allows you to create and manage payment requests (to receive funds) and send links (to send funds).

📝 Prerequisites: Before using Payment Links, ensure you have:

  1. Deployed your widget from the sphere-widget template
  2. Registered the widget URL in your Sphere dashboard
  3. Registered platform-specific redirect URLs for web, mobile, and/or Telegram

Without proper widget setup, payment links won't work across platforms. See the Project Setup section above.

import {
ChainName /*, add other relevant types like TokenSymbol if used */,
} from "@stratosphere-network/wallet"; // Adjust import path if needed

async function paymentRequestsAndSendLinksExamples() {
if (!sphere.isAuthenticated()) {
console.log("Please authenticate the user first.");
return;
}

try {
// --- Payment Requests ---

// 1. Create a Payment Request (to receive funds)
console.log("\n--- Creating Payment Request ---");
const paymentRequestInput = {
amount: 10, // Amount in token units (e.g., 10 USDC)
chain: ChainName.POLYGON, // Example chain
token: "USDC" as const, // Ensure this token is supported
};
const createdRequest = await sphere.paymentLinks.requestPayment(
paymentRequestInput
);
const paymentNonce = createdRequest.data; // Nonce for the payment request
console.log("Payment Request Created. Nonce:", paymentNonce);

// 2. Get the redirect URL for the payer
const redirectUrl = sphere.paymentLinks.getPaymentRedirectUrl(paymentNonce);
console.log("Payer can use this URL:", redirectUrl);

// 3. Pay a Payment Request (simulating a payer action)
// Note: In a real scenario, the payer would open the redirectUrl.
// This example shows direct execution if the SDK user (e.g., backend) needs to pay it.
console.log("\n--- Paying Payment Request ---");
// Ensure the payer has sufficient funds and is authenticated if required by your setup.
// For this example, we assume the current SDK user is paying.
const payResponse = await sphere.paymentLinks.payPaymentRequest(
paymentNonce
);
console.log("Payment Request Paid. Response:", payResponse);

// --- Send Links ---

// 4. Create a Specific Send Link (to a specific recipient)
console.log("\n--- Creating Specific Send Link ---");
const specificSendLinkInput = {
value: "5", // Amount as a string
token: "USDT" as const,
chain: ChainName.ETHEREUM,
receiver: "0xRecipientAddress", // The specific address that can claim
time: "1h", // Expires in 1 hour
};
const createdSpecificLink =
await sphere.paymentLinks.createSpecificSendLink(specificSendLinkInput);
const specificLinkUrlId = createdSpecificLink.data; // URL ID for the send link
console.log("Specific Send Link Created. URL ID:", specificLinkUrlId);
// The link to share would be constructed using this urlId, e.g., your-app.com/claim?id=specificLinkUrlId

// 5. Create an Open Send Link (anyone with the link can claim)
console.log("\n--- Creating Open Send Link ---");
const openSendLinkInput = {
value: "2",
token: "WBERA" as const, // Assuming WBERA is a supported token
chain: ChainName.BERACHAIN, // Example chain
time: "30m", // Expires in 30 minutes
};
const createdOpenLink = await sphere.paymentLinks.createOpenSendLink(
openSendLinkInput
);
const openLinkUrlId = createdOpenLink.data;
console.log("Open Send Link Created. URL ID:", openLinkUrlId);

// 6. Claim an Open Send Link (simulating a claimer action)
console.log("\n--- Claiming Open Send Link ---");
const claimOpenLinkInput = {
id: openLinkUrlId, // The URL ID of the send link
to: "0xClaimerAddress", // Address where the funds should go
};
const claimOpenResponse = await sphere.paymentLinks.claimOpenSendLink(
claimOpenLinkInput
);
console.log("Open Send Link Claimed. Response:", claimOpenResponse);

// Assuming you might want to claim a specific link as well:
// const claimSpecificLinkInput = {
// id: specificLinkUrlId, // The URL ID of the specific send link
// to: "0xRecipientAddress", // Must be the designated receiver
// };
// const claimSpecificResponse = await sphere.paymentLinks.claimSpecificSendLink(claimSpecificLinkInput);
// console.log("Specific Send Link Claimed. Response:", claimSpecificResponse);

// --- Listing ---
console.log("\n--- Listing Payment Requests ---");
const paymentRequestsList = await sphere.paymentLinks.listPaymentRequests({
limit: "5",
});
console.log("Payment Requests:", paymentRequestsList.data);

console.log("\n--- Listing Send Links ---");
const sendLinksList = await sphere.paymentLinks.listSendLinks({
limit: "5",
});
console.log("Send Links:", sendLinksList.data);

// --- Cancellation ---
// (Ensure the links/requests exist and are cancellable by the current user)

// 7. Cancel a Payment Request (if not paid)
console.log("\n--- Cancelling Payment Request ---");
// Re-create a new one for cancellation example to avoid issues if already paid
const newRequestToCancel = await sphere.paymentLinks.requestPayment(
paymentRequestInput
);
const nonceToCancel = newRequestToCancel.data;
console.log("New request to cancel created with nonce:", nonceToCancel);
const cancelPayReqResponse = await sphere.paymentLinks.cancelPaymentRequest(
nonceToCancel
);
console.log("Payment Request Cancelled. Response:", cancelPayReqResponse);

// 8. Cancel a Send Link (if not claimed/expired)
console.log("\n--- Cancelling Send Link ---");
// Re-create a new one for cancellation example
const newOpenLinkToCancel = await sphere.paymentLinks.createOpenSendLink(
openSendLinkInput
);
const urlIdToCancel = newOpenLinkToCancel.data;
console.log("New open link to cancel created with URL ID:", urlIdToCancel);
const cancelSendLinkResponse = await sphere.paymentLinks.cancelSendLink(
urlIdToCancel
);
console.log("Send Link Cancelled. Response:", cancelSendLinkResponse);
} catch (error) {
console.error("Payment Links Service Error:", error);
}
}

// Make sure user is authenticated before calling
// userAuth().then(paymentRequestsAndSendLinksExamples);

DeFi Swaps

Perform token swaps using integrated DeFi protocols.

import { ChainName, SwapFlow } from "@stratosphere-network/wallet"; // Adjust import path if needed

async function defiSwapExamples() {
if (!sphere.isAuthenticated()) {
console.log("Please authenticate the user first.");
return;
}

try {
const swapDetails = {
chain: ChainName.POLYGON,
flow: SwapFlow.NORMAL, // or SwapFlow.GASLESS
token_to_sell: "MATIC", // TokenSymbol, ensure it's defined or use string
token_to_buy: "USDC", // TokenSymbol, ensure it's defined or use string
value: "1", // Amount of token_to_sell
// token_to_sell_address: 'OPTIONAL_CONTRACT_ADDRESS',
// token_to_buy_address: 'OPTIONAL_CONTRACT_ADDRESS',
};

const swapResponse = await sphere.defi.swap(swapDetails);
console.log("DeFi Swap Response:", swapResponse);
} catch (error) {
console.error("DeFi Swap Error:", error);
}
}

// Make sure user is authenticated before calling
// userAuthExample().then(defiSwapExamples);

Proxy Wallet & External Contract Interactions

For advanced use cases that require direct interaction with smart contracts, transaction signing control, or integration with external blockchain libraries, Sphere provides the Proxy Wallet Service. This service gives you low-level access to wallet functionality while maintaining the security and convenience of the Sphere platform.

import { ChainName } from "@stratosphere-network/wallet"; // Adjust import path if needed

async function proxyWalletExamples() {
if (!sphere.isAuthenticated()) {
console.log("Please authenticate the user first.");
return;
}

try {
// Get wallet instance information for a specific chain
const walletInstance = await sphere.proxyWallet.getWalletInstance({
chain: ChainName.ETHEREUM,
});
console.log("Wallet Instance:", walletInstance);
console.log("Address:", walletInstance.address);
console.log("Chain ID:", walletInstance.chain.id);
console.log("Provider URL:", walletInstance.provider.url);

// Sign a message for authentication or ownership proof
const message = "Welcome to MyApp! Please sign to authenticate.";
const signedMessage = await sphere.proxyWallet.signMessage({
chain: ChainName.ETHEREUM,
message: message,
});
console.log("Message Signature:", signedMessage.signature);
console.log("Signer Address:", signedMessage.signer);

// Sign a transaction without broadcasting it
const transactionData = {
to: "0xRecipientAddress",
value: "1000000000000000000", // 1 ETH in wei
gasLimit: "21000",
gasPrice: "20000000000", // 20 Gwei
chainId: 1,
};

const signedTx = await sphere.proxyWallet.signTransaction({
chain: ChainName.ETHEREUM,
transactionData: transactionData,
});
console.log("Signed Transaction:", signedTx.signedTransaction);
console.log("Transaction Hash:", signedTx.txHash);

// Send a transaction with full control over gas parameters
const sentTx = await sphere.proxyWallet.sendTransaction({
chain: ChainName.POLYGON,
transactionData: {
to: "0xRecipientAddress",
value: "100000000000000000", // 0.1 MATIC in wei
gasLimit: "21000",
gasPrice: "30000000000", // 30 Gwei
chainId: 137,
},
});
console.log("Transaction Sent:", sentTx.hash);
console.log("Block Number:", sentTx.blockNumber);
} catch (error) {
console.error("Proxy Wallet Error:", error);
}
}

// Make sure user is authenticated before calling
// userAuth().then(proxyWalletExamples);

Use Cases for Proxy Wallet Service:

  • Smart Contract Integration: Interact with custom smart contracts that aren't directly supported by other Sphere services
  • Advanced DeFi Operations: Perform complex multi-step DeFi operations with precise transaction control
  • Authentication Systems: Use message signing for secure user authentication and address ownership verification
  • Batch Operations: Sign multiple transactions for later batch submission
  • External Library Integration: Use wallet instance information with popular libraries like ethers.js or web3.js
  • Custom Gas Management: Have full control over gas prices and transaction parameters

Note: The Proxy Wallet Service provides direct blockchain access and requires understanding of transaction structures and gas mechanics. For simpler use cases, consider using the standard transaction service which provides gasless operations.

For detailed documentation on all Proxy Wallet methods, parameters, and advanced examples, see the Proxy Wallet & External Contracts documentation.

Frontend Example: React UI

Here's how you might handle the Sphere SDK authentication flow within a React application. This example demonstrates managing state for the OTP process with the direct authentication response.

Assumptions:

  • You have the Sphere SDK installed (npm install sphere-network-sdk or yarn add sphere-network-sdk).
  • Sphere and Environment are importable (adjust path if needed).

1. Sphere SDK Initialization (e.g., in src/services/sphereClient.ts):

// src/services/sphereClient.ts
import Sphere, { Environment } from "@stratosphere-network/wallet"; // Adjust path as needed

export const sphere = new Sphere({
environment: Environment.DEVELOPMENT, // Or Environment.PRODUCTION
// apiKey: "YOUR_PROJECT_API_KEY", // If needed for other parts of your app
});

// You can optionally connect to events service for other real-time features
// sphere.events.connect()
// .then(() => console.log("React App: Connected to Sphere Event Service"))
// .catch((error) => console.error("Failed to connect to Event Service:", error));

2. React Authentication Component (e.g., src/components/AuthComponent.tsx):

// src/components/AuthComponent.tsx
import React, { useState, useEffect } from "react";
import { sphere } from "../services/sphereClient"; // Adjust path
import { LoginResponse, User } from "@stratosphere-network/wallet"; // Adjust path

type AuthMethod = "phone" | "email" | "external";

const AuthComponent: React.FC = () => {
const [authMethod, setAuthMethod] = useState<AuthMethod>("phone");
const [phoneNumber, setPhoneNumber] = useState<string>("");
const [email, setEmail] = useState<string>("");
const [externalId, setExternalId] = useState<string>("");
const [password, setPassword] = useState<string>("");
const [otp, setOtp] = useState<string>("");
const [isOtpSent, setIsOtpSent] = useState<boolean>(false);
const [isLoading, setIsLoading] = useState<boolean>(false);
const [error, setError] = useState<string | null>(null);
const [currentUser, setCurrentUser] = useState<User | null>(null);
const [isAuthenticated, setIsAuthenticated] = useState<boolean>(
sphere.auth.isAuthenticated()
);

useEffect(() => {
// Check initial auth state
if (sphere.auth.isAuthenticated()) {
sphere.auth
.getUser()
.then((userResponse) => {
setCurrentUser(userResponse.user);
setIsAuthenticated(true);
})
.catch(() => {
sphere.auth.logout();
setIsAuthenticated(false);
});
}
}, []);

const handleSignup = async () => {
setIsLoading(true);
setError(null);

try {
if (authMethod === "phone") {
if (!phoneNumber) {
setError("Phone number is required for phone authentication.");
return;
}
// ⚠️ Phone ONLY - Do NOT provide email or externalId
await sphere.auth.signup({
phoneNumber,
// email: email, // ❌ NEVER provide with phoneNumber
// externalId: externalId, // ❌ NEVER provide with phoneNumber
});
await handleSendOtp();
} else if (authMethod === "email") {
if (!email) {
setError("Email is required for email authentication.");
return;
}
// ⚠️ Email ONLY - Do NOT provide phone or externalId
await sphere.auth.signup({
email,
// phoneNumber: phoneNumber, // ❌ NEVER provide with email
// externalId: externalId, // ❌ NEVER provide with email
});
await handleSendOtp();
} else if (authMethod === "external") {
if (!externalId || !password) {
setError(
"External ID and password are required for external authentication."
);
return;
}
// ⚠️ External ID + Password ONLY - Do NOT provide phone or email
await sphere.auth.signup({
externalId,
password,
// phoneNumber: phoneNumber, // ❌ NEVER provide with externalId
// email: email, // ❌ NEVER provide with externalId
});
// No OTP needed for external ID authentication
await handleLogin();
}
} catch (err: any) {
setError(err.message || "Failed to signup.");
setIsLoading(false);
}
};

const handleSendOtp = async () => {
if (authMethod === "external") return; // No OTP for external auth

setIsLoading(true);
setError(null);

try {
if (authMethod === "phone") {
if (!phoneNumber) {
setError("Phone number is required.");
return;
}
// ⚠️ Phone ONLY - Do NOT provide email
await sphere.auth.sendOtp({
phone: phoneNumber,
// email: email, // ❌ NEVER provide with phone
});
} else if (authMethod === "email") {
if (!email) {
setError("Email is required.");
return;
}
// ⚠️ Email ONLY - Do NOT provide phone
await sphere.auth.sendOtp({
email,
// phone: phoneNumber, // ❌ NEVER provide with email
});
}

setIsOtpSent(true);
setIsLoading(false);
} catch (err: any) {
setError(err.message || "Failed to send OTP.");
setIsLoading(false);
}
};

const handleLogin = async () => {
setIsLoading(true);
setError(null);

try {
let loginResponse: LoginResponse;

if (authMethod === "phone") {
if (!otp) {
setError("OTP is required for phone authentication.");
return;
}
// ⚠️ Phone + OTP ONLY - Do NOT provide email or externalId
loginResponse = await sphere.auth.login({
phoneNumber,
otpCode: otp,
// email: email, // ❌ NEVER provide with phoneNumber
// externalId: externalId, // ❌ NEVER provide with phoneNumber
});
} else if (authMethod === "email") {
if (!otp) {
setError("OTP is required for email authentication.");
return;
}
// ⚠️ Email + OTP ONLY - Do NOT provide phone or externalId
loginResponse = await sphere.auth.login({
email,
otpCode: otp,
// phoneNumber: phoneNumber, // ❌ NEVER provide with email
// externalId: externalId, // ❌ NEVER provide with email
});
} else if (authMethod === "external") {
if (!externalId || !password) {
setError("External ID and password are required.");
return;
}
// ⚠️ External ID + Password ONLY - Do NOT provide phone or email
loginResponse = await sphere.auth.login({
externalId,
password,
// phoneNumber: phoneNumber, // ❌ NEVER provide with externalId
// email: email, // ❌ NEVER provide with externalId
});
} else {
setError("Invalid authentication method.");
return;
}

console.log("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
localStorage.setItem("sphereAccessToken", loginResponse.accessToken);

setIsAuthenticated(true);

// Fetch full user details
const userDetails = await sphere.auth.getUser();
setCurrentUser(userDetails.user);

setIsLoading(false);
setError(null);
} catch (err: any) {
setError(err.message || "Login failed.");
setIsLoading(false);
}
};

const handleLogout = () => {
sphere.auth.logout();
localStorage.removeItem("sphereAccessToken");
setIsAuthenticated(false);
setCurrentUser(null);
setIsOtpSent(false);
setOtp("");
setPhoneNumber("");
setEmail("");
setExternalId("");
setPassword("");
};

if (isAuthenticated && currentUser) {
return (
<div>
<h2>Welcome, {currentUser.email || currentUser.phoneNumber}!</h2>
<p>Your User ID: {currentUser.id}</p>
<button onClick={handleLogout}>Logout</button>
{/* Add other authenticated content here */}
</div>
);
}

return (
<div>
<h2>Sphere SDK Auth</h2>

{/* Authentication Method Selection */}
<div>
<label>
<input
type="radio"
value="phone"
checked={authMethod === "phone"}
onChange={(e) => setAuthMethod(e.target.value as AuthMethod)}
/>
Phone + OTP
</label>
<label>
<input
type="radio"
value="email"
checked={authMethod === "email"}
onChange={(e) => setAuthMethod(e.target.value as AuthMethod)}
/>
Email + OTP
</label>
<label>
<input
type="radio"
value="external"
checked={authMethod === "external"}
onChange={(e) => setAuthMethod(e.target.value as AuthMethod)}
/>
External ID + Password
</label>
</div>

{!isOtpSent ? (
<div>
{authMethod === "phone" && (
<div>
<label htmlFor="phoneNumber">Phone Number:</label>
<input
type="tel"
id="phoneNumber"
value={phoneNumber}
onChange={(e) => setPhoneNumber(e.target.value)}
placeholder="+12345678900"
/>
</div>
)}

{authMethod === "email" && (
<div>
<label htmlFor="email">Email:</label>
<input
type="email"
id="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="user@example.com"
/>
</div>
)}

{authMethod === "external" && (
<div>
<label htmlFor="externalId">External ID:</label>
<input
type="text"
id="externalId"
value={externalId}
onChange={(e) => setExternalId(e.target.value)}
placeholder="username or email"
/>
<label htmlFor="password">Password:</label>
<input
type="password"
id="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="Password"
/>
</div>
)}

<button onClick={handleSignup} disabled={isLoading}>
{isLoading
? "Processing..."
: `Sign Up with ${
authMethod === "phone"
? "Phone"
: authMethod === "email"
? "Email"
: "External ID"
}`}
</button>
</div>
) : (
<div>
<label htmlFor="otp">OTP Code:</label>
<input
type="text"
id="otp"
value={otp}
onChange={(e) => setOtp(e.target.value)}
placeholder="123456"
/>
<button onClick={handleLogin} disabled={isLoading}>
{isLoading ? "Logging in..." : "Login"}
</button>
</div>
)}

{error && <p style={{ color: "red" }}>Error: {error}</p>}
</div>
);
};

export default AuthComponent;

3. Using the Component (e.g., in src/App.tsx):

// src/App.tsx
import React, { useEffect } from "react";
import AuthComponent from "./components/AuthComponent"; // Adjust path
import { sphere } from "./services/sphereClient"; // Adjust path

function App() {
useEffect(() => {
// Check if we have a saved token from a previous session
const savedToken = localStorage.getItem("sphereAccessToken");
if (savedToken) {
sphere.setBearerToken(savedToken);
}
}, []);

return (
<div className="App">
<header className="App-header">
<h1>My App with Sphere SDK</h1>
</header>
<main>
<AuthComponent />
{/* Other components that might use sphere SDK can be added here */}
{/* Example: <WalletInfoComponent /> */}
</main>
</div>
);
}

export default App;

Key points for the React example:

  • Direct Response: The login method now returns a LoginResponse with all necessary data
  • Simplified Flow: No need for event listeners or callbacks - authentication is synchronous
  • Token Persistence: The example shows saving the token to localStorage for session persistence
  • State Management: Uses useState to handle UI state in a straightforward manner
  • Error Handling: Direct try-catch blocks make error handling simpler
  • Auto Token Setting: The SDK automatically sets the bearer token when login succeeds

Next Steps

This quickstart guide covered the basics of getting started with the Sphere SDK. For more detailed information on specific services, methods, and types, please refer to:

We are constantly improving our SDKs and documentation. If you have any questions or feedback, please don't hesitate to reach out or open an issue on our GitHub repository.

Happy building!