Skip to main content

ParentProvider

The ParentProvider component connects your game frontend to the Upside platform and provides authentication.
import { ParentProvider, useParent, useBalance, useToken, useAuthenticatedFetch } from "@b3dotfun/upside-sdk";

export default function CoinFlipGame() {
  return (
    <ParentProvider>
      <GameContent />
    </ParentProvider>
  );
}

function GameContent() {
  const { token, balance, showWinModal, showLossModal, refetchBalance } = useParent();

  // Or use individual hooks:
  const balance = useBalance(); // number | null
  const token = useToken(); // string | null
  const authFetch = useAuthenticatedFetch(); // fetch with Authorization header

  return (
    <div>
      <h1>Coin Flip</h1>
      <p>Balance: {(balance / 1e18).toFixed(2)} WIN</p>
      <button onClick={() => playGame(token)}>Play</button>
    </div>
  );
}

Available Hooks

useParent()

Access full context (token, balance, showWinModal, showLossModal, etc.) Return Values:
  • token (string | null): JWT authentication token for backend calls
  • balance (number | null): Current WIN token balance in wei
  • showWinModal(wins: string): Show win modal
  • showLossModal(loss: string): Show loss modal
  • showToast(options): Show toast notification
  • showCustomModal(content): Show custom modal
  • refetchBalance(): Request balance refresh from platform

useBalance()

Get player balance (number | null)
const balance = useBalance();

useToken()

Get authentication token (string | null)
const token = useToken();

useRefetchBalance()

Function to request balance refresh
const refetchBalance = useRefetchBalance();

useCustomModal()

Function to show custom modals
const showCustomModal = useCustomModal();

useAuthenticatedFetch()

Fetch function with automatic Bearer token header
const authFetch = useAuthenticatedFetch();

Making API Calls

Use the useAuthenticatedFetch() hook to make authenticated requests:
import { useAuthenticatedFetch, useBalance, useToken } from "@b3dotfun/upside-sdk";

function GameComponent() {
  const authFetch = useAuthenticatedFetch();
  const balance = useBalance();
  const token = useToken();

  const playGame = async prediction => {
    // Authorization header is automatically added
    const response = await authFetch("/api/game/coin-flip", {
      method: "POST",
      body: JSON.stringify({
        playerId: "player-id",
        prediction,
        betAmount: "1000000000000000000", // 1 WIN in wei
      }),
    });

    const data = await response.json();
    return data;
  };

  return (
    <div>
      <p>Balance: {(balance / 1e18).toFixed(2)} WIN</p>
      <button onClick={() => playGame("heads")}>Predict Heads</button>
    </div>
  );
}
How useAuthenticatedFetch() Works:
  • Automatically includes Authorization: Bearer {token} header
  • Sets Content-Type: application/json by default
  • Merges additional headers you provide
  • Handles token from useToken() automatically

Frontend Example

import { ParentProvider, useParentContext } from "@b3dotfun/upside-sdk";

export default function CoinFlipGame() {
  return (
    <ParentProvider>
      <CoinFlipContent />
    </ParentProvider>
  );
}

function CoinFlipContent() {
  const { token, balance, playerId } = useParentContext();
  const [gameState, setGameState] = useState("ready"); // ready, playing, won, lost
  const [prediction, setPrediction] = useState(null);
  const [result, setResult] = useState(null);
  const [earnings, setEarnings] = useState(0);

  const playGame = async playerPrediction => {
    setPrediction(playerPrediction);
    setGameState("playing");

    try {
      // Call your backend
      const response = await fetch("/api/game/coin-flip", {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          Authorization: `Bearer ${token}`,
        },
        body: JSON.stringify({
          playerId,
          prediction: playerPrediction,
          betAmount: "100000000000000000", // 1 token in wei
        }),
      });

      const data = await response.json();

      if (data.outcome === "win") {
        setGameState("won");
        setEarnings(data.payout);
      } else {
        setGameState("lost");
        setEarnings(0);
      }

      setResult(data.result);
    } catch (error) {
      console.error("Game error:", error);
      setGameState("error");
    }
  };

  return (
    <div style={{ textAlign: "center", padding: "40px" }}>
      <h1>Coin Flip</h1>
      <p>Balance: {(balance / 1e18).toFixed(2)} WIN</p>

      {gameState === "ready" && (
        <div>
          <button onClick={() => playGame("heads")}>Predict Heads</button>
          <button onClick={() => playGame("tails")}>Predict Tails</button>
        </div>
      )}

      {gameState === "playing" && <p>Flipping...</p>}

      {gameState === "won" && (
        <div>
          <p>🎉 You won! Coin landed on {result}</p>
          <p>+{(earnings / 1e18).toFixed(2)} WIN</p>
        </div>
      )}

      {gameState === "lost" && (
        <div>
          <p>❌ You lost! Coin landed on {result}</p>
          <p>Better luck next time</p>
        </div>
      )}

      {gameState !== "ready" && <button onClick={() => setGameState("ready")}>Play Again</button>}
    </div>
  );
}