Skip to main content

MCAP SDK (Under Development)

Overview

The MCAP SDK provides a comprehensive toolkit for integrating memecoin functionality into your application. Whether you're using existing MCAP platform games or building custom experiences, the SDK handles all the complex smart contract interactions while providing a simple, developer-friendly interface.

SDK Features

🔐 Core Infrastructure

  • Wallet Connection & Authentication: Seamless Web3 wallet integration with signature verification
  • Multi-Token Support: Accept any ERC-20 token without managing individual smart contract integrations
  • Session Management: Secure user sessions with automatic renewal
  • Real-time Balance Tracking: Live balance updates across multiple tokens

💰 Financial Operations

  • Deposit Management: One-click token deposits with smart contract approval handling
  • Withdrawal Processing: Instant withdrawal processing with status tracking
  • Transaction History: Complete transaction and gaming history for users
  • Settlement & Payouts: Automated on-chain settlement and payout systems

🎮 Gaming Integration

  • Betting Infrastructure: Place and settle bets with cryptographic proofs
  • Multiplayer Features: Head-to-head matches with locked funds

📊 Analytics & Monitoring

  • User Analytics: Track user behavior and engagement
  • Performance Metrics: Monitor platform performance and usage
  • Custom Events: Track platform-specific events and actions
  • Real-time Monitoring: Live data and status updates

Installation

NPM Package Installation

# Platform SDK for using existing MCAP games
npm install @mcap/platform-sdk

# TypeScript definitions
npm install @types/mcap-sdk

Basic Configuration

Environment Setup

# .env file
MCAP_PROVIDER_ID=your-provider-id
MCAP_VAULT_ADDRESS=0x...your-non-custodial-vault-address
MCAP_BASE_URL=https://mcap-api.mcap.meme
MCAP_ENVIRONMENT=production
MCAP_GAME_ID=your-game-id

SDK Initialization

For Custom Development

// mcap-config.js
import { MCAPDeveloper } from '@mcap/dev-sdk';

export const mcap = new MCAPDeveloper({
providerId: process.env.MCAP_PROVIDER_ID,
vaultAddress: process.env.MCAP_VAULT_ADDRESS,
baseURL: process.env.MCAP_BASE_URL,
environment: process.env.MCAP_ENVIRONMENT,
gameId: process.env.MCAP_GAME_ID
});

// Initialize with custom configuration
await mcap.initialize();

For Platform Integration

// mcap-platform-config.js
import { MCAPPlatform } from '@mcap/platform-sdk';

export const mcapPlatform = new MCAPPlatform({
providerId: process.env.MCAP_PROVIDER_ID,
apiKey: process.env.MCAP_API_KEY,
environment: process.env.MCAP_ENVIRONMENT,
theme: {
primaryColor: '#your-brand-color',
secondaryColor: '#your-secondary-color',
logoUrl: '/path/to/your/logo.png',
brandName: 'Your Platform Name'
}
});

// Initialize platform
await mcapPlatform.initialize();

Configuration Validation

// Verify configuration on startup
mcap.validateConfig()
.then(() => console.log('✅ MCAP configured successfully'))
.catch(err => {
console.error('❌ MCAP configuration error:', err);
process.exit(1);
});

Core SDK Features

1. Wallet Connection & Authentication

This section demonstrates how to integrate Web3 wallet connections with MCAP's authentication system. Users connect their wallets, sign a cryptographic message to prove ownership, and receive JWT tokens for authenticated API access.

React Hook Implementation

// hooks/useWalletConnection.js
import { useState, useEffect } from 'react';
import { useWallet } from '@mcap/platform-sdk';
import { mcap } from '../mcap-config';

export const useWalletConnection = () => {
const { connect, disconnect, account, isConnected } = useWallet();
const [isAuthenticated, setIsAuthenticated] = useState(false);
const [user, setUser] = useState(null);
const [isAuthenticating, setIsAuthenticating] = useState(false);

const authenticate = async () => {
if (!isConnected || !account) return;

setIsAuthenticating(true);
try {
// Generate nonce for signing
const { nonce } = await mcap.auth.generateNonce(account);

// Request signature from wallet
const signature = await window.ethereum.request({
method: 'personal_sign',
params: [nonce, account]
});

// Exchange signature for JWT
const authResult = await mcap.auth.login({
wallet_address: account,
signature: signature,
nonce: nonce
});

// Store authentication state
setUser(authResult.user);
setIsAuthenticated(true);
localStorage.setItem('mcap_jwt', authResult.access_token);
localStorage.setItem('mcap_refresh_token', authResult.refresh_token);

return authResult;

} catch (error) {
console.error('Authentication failed:', error);
throw error;
} finally {
setIsAuthenticating(false);
}
};

const logout = () => {
setUser(null);
setIsAuthenticated(false);
localStorage.removeItem('mcap_jwt');
localStorage.removeItem('mcap_refresh_token');
disconnect();
};

useEffect(() => {
if (isConnected && account && !isAuthenticated) {
authenticate();
}
}, [isConnected, account]);

return {
connect,
disconnect,
logout,
authenticate,
account,
isConnected,
isAuthenticated,
isAuthenticating,
user
};
};

Wallet Connection Component

// components/WalletConnection.jsx
import React from 'react';
import { useWalletConnection } from '../hooks/useWalletConnection';

const WalletConnection = ({ onAuthenticated }) => {
const {
connect,
logout,
account,
isConnected,
isAuthenticated,
isAuthenticating,
user
} = useWalletConnection();

useEffect(() => {
if (isAuthenticated && user) {
onAuthenticated(user);
}
}, [isAuthenticated, user]);

if (!isConnected) {
return (
<button onClick={connect} className="connect-wallet-btn">
Connect Wallet
</button>
);
}

if (!isAuthenticated) {
return (
<div className="authenticating">
{isAuthenticating ? (
<div>Authenticating...</div>
) : (
<div>Please sign the message to continue</div>
)}
</div>
);
}

return (
<div className="wallet-connected">
<div className="user-info">
<span>Connected: {account.slice(0, 6)}...{account.slice(-4)}</span>
{user?.username && <span>Welcome, {user.username}!</span>}
</div>
<button onClick={logout} className="logout-btn">
Disconnect
</button>
</div>
);
};

export default WalletConnection;

2. Deposit Management

Learn how to handle ERC20 token deposits into MCAP's vault system. This covers approving token spending, executing deposits through smart contracts, and updating user balances for immediate gameplay use.

Deposit Hook

// hooks/useDeposits.js
import { useState, useEffect } from 'react';
import { mcap } from '../mcap-config';
import { ethers } from 'ethers';

export const useDeposits = () => {
const [balance, setBalance] = useState(null);
const [isDepositing, setIsDepositing] = useState(false);

const getBalance = async (tokenId) => {
try {
const jwt = localStorage.getItem('mcap_jwt');
const balanceData = await mcap.wallets.getBalance(jwt, tokenId);
setBalance(balanceData);
return balanceData;
} catch (error) {
console.error('Failed to fetch balance:', error);
throw error;
}
};

const deposit = async ({ tokenId, amount, tokenAddress }) => {
setIsDepositing(true);
try {
// Get provider and signer
const provider = new ethers.providers.Web3Provider(window.ethereum);
const signer = provider.getSigner();

// Create token contract instance
const tokenContract = new ethers.Contract(
tokenAddress,
['function approve(address spender, uint256 amount) external returns (bool)'],
signer
);

// Create vault contract instance
const vaultContract = new ethers.Contract(
mcap.config.vaultAddress,
['function deposit(address token, uint256 amount) external'],
signer
);

// Step 1: Approve token spend
const approveTx = await tokenContract.approve(
mcap.config.vaultAddress,
amount
);
await approveTx.wait();

// Step 2: Deposit to vault
const depositTx = await vaultContract.deposit(tokenAddress, amount);
await depositTx.wait();

// Step 3: Wait for MCAP to detect deposit
await new Promise(resolve => setTimeout(resolve, 5000));

// Step 4: Refresh balance
await getBalance(tokenId);

return {
txHash: depositTx.hash,
amount: amount,
tokenId: tokenId
};

} catch (error) {
console.error('Deposit failed:', error);
throw error;
} finally {
setIsDepositing(false);
}
};

return {
balance,
isDepositing,
getBalance,
deposit
};
};

Deposit Component

// components/DepositManager.jsx
import React, { useState, useEffect } from 'react';
import { useDeposits } from '../hooks/useDeposits';
import { ethers } from 'ethers';

const DepositManager = ({ selectedToken, onDepositSuccess }) => {
const [amount, setAmount] = useState('');
const { balance, isDepositing, getBalance, deposit } = useDeposits();

useEffect(() => {
if (selectedToken) {
getBalance(selectedToken.id);
}
}, [selectedToken]);

const handleDeposit = async () => {
if (!amount || !selectedToken) return;

try {
const amountWei = ethers.utils.parseUnits(amount, selectedToken.decimals);

const result = await deposit({
tokenId: selectedToken.id,
amount: amountWei.toString(),
tokenAddress: selectedToken.address
});

onDepositSuccess(result);
setAmount('');

} catch (error) {
alert('Deposit failed: ' + error.message);
}
};

const formatBalance = (balanceWei) => {
if (!balanceWei) return '0';
return ethers.utils.formatUnits(balanceWei, selectedToken?.decimals || 18);
};

return (
<div className="deposit-manager">
<h3>Deposit {selectedToken?.symbol}</h3>

<div className="current-balance">
Current Balance: {formatBalance(balance?.available_balance)} {selectedToken?.symbol}
</div>

<div className="deposit-form">
<input
type="number"
value={amount}
onChange={(e) => setAmount(e.target.value)}
placeholder={`Amount in ${selectedToken?.symbol}`}
disabled={isDepositing}
/>

<button
onClick={handleDeposit}
disabled={isDepositing || !amount}
className="deposit-btn"
>
{isDepositing ? 'Depositing...' : 'Deposit'}
</button>
</div>
</div>
);
};

export default DepositManager;

3. Withdrawal System

Implement secure token withdrawals from MCAP back to user wallets. This section shows how to request withdrawals, monitor processing status, and handle the complete withdrawal lifecycle.

Withdrawal Hook

// hooks/useWithdrawals.js
import { useState } from 'react';
import { mcap } from '../mcap-config';

export const useWithdrawals = () => {
const [isWithdrawing, setIsWithdrawing] = useState(false);

const withdraw = async ({ tokenId, amount }) => {
setIsWithdrawing(true);
try {
const jwt = localStorage.getItem('mcap_jwt');

const withdrawalResult = await mcap.wallets.withdraw({
token_id: tokenId,
amount: amount
}, jwt);

return withdrawalResult;

} catch (error) {
console.error('Withdrawal failed:', error);
throw error;
} finally {
setIsWithdrawing(false);
}
};

const getWithdrawalStatus = async (withdrawalId) => {
try {
const jwt = localStorage.getItem('mcap_jwt');
return await mcap.wallets.getWithdrawalStatus(withdrawalId, jwt);
} catch (error) {
console.error('Failed to get withdrawal status:', error);
throw error;
}
};

return {
isWithdrawing,
withdraw,
getWithdrawalStatus
};
};

Withdrawal Component

// components/WithdrawalManager.jsx
import React, { useState } from 'react';
import { useWithdrawals } from '../hooks/useWithdrawals';
import { ethers } from 'ethers';

const WithdrawalManager = ({ selectedToken, availableBalance, onWithdrawSuccess }) => {
const [amount, setAmount] = useState('');
const { isWithdrawing, withdraw, getWithdrawalStatus } = useWithdrawals();

const handleWithdraw = async () => {
if (!amount || !selectedToken) return;

try {
const amountWei = ethers.utils.parseUnits(amount, selectedToken.decimals);
const availableWei = ethers.BigNumber.from(availableBalance || '0');

if (amountWei.gt(availableWei)) {
throw new Error('Insufficient available balance');
}

const withdrawalResult = await withdraw({
tokenId: selectedToken.id,
amount: amountWei.toString()
});

// Poll for completion
const pollStatus = async () => {
const status = await getWithdrawalStatus(withdrawalResult.withdrawal_id);

if (status.status === 'completed') {
onWithdrawSuccess({
amount: amountWei.toString(),
txHash: status.tx_hash,
withdrawalId: withdrawalResult.withdrawal_id
});
setAmount('');
} else if (status.status === 'failed') {
throw new Error('Withdrawal failed: ' + status.error);
} else {
setTimeout(pollStatus, 5000);
}
};

setTimeout(pollStatus, 5000);

} catch (error) {
alert('Withdrawal failed: ' + error.message);
}
};

const maxAmount = ethers.utils.formatUnits(
availableBalance || '0',
selectedToken?.decimals || 18
);

return (
<div className="withdrawal-manager">
<h3>Withdraw {selectedToken?.symbol}</h3>

<div className="available-balance">
Available: {maxAmount} {selectedToken?.symbol}
</div>

<div className="withdrawal-form">
<input
type="number"
value={amount}
onChange={(e) => setAmount(e.target.value)}
placeholder="Amount to withdraw"
max={maxAmount}
disabled={isWithdrawing}
/>

<div className="withdrawal-actions">
<button
onClick={() => setAmount(maxAmount)}
disabled={isWithdrawing || maxAmount === '0'}
className="max-btn"
>
Max
</button>

<button
onClick={handleWithdraw}
disabled={isWithdrawing || !amount}
className="withdraw-btn"
>
{isWithdrawing ? 'Processing...' : 'Withdraw'}
</button>
</div>
</div>
</div>
);
};

export default WithdrawalManager;

4. Play History & Analytics

Build comprehensive user activity tracking with transaction history, betting records, and performance analytics. This section covers fetching user data, displaying statistics, and creating detailed activity feeds.

History Hook

// hooks/useHistory.js
import { useState } from 'react';
import { mcap } from '../mcap-config';

export const useHistory = () => {
const [loading, setLoading] = useState(false);

const getTransactionHistory = async (filters = {}) => {
setLoading(true);
try {
const jwt = localStorage.getItem('mcap_jwt');
return await mcap.history.getTransactions({
...filters,
jwt
});
} catch (error) {
console.error('Failed to fetch transaction history:', error);
throw error;
} finally {
setLoading(false);
}
};

const getBetHistory = async (filters = {}) => {
setLoading(true);
try {
const jwt = localStorage.getItem('mcap_jwt');
return await mcap.history.getBets({
...filters,
jwt
});
} catch (error) {
console.error('Failed to fetch bet history:', error);
throw error;
} finally {
setLoading(false);
}
};

const getGameStats = async (userId, timeRange = '30d') => {
try {
const jwt = localStorage.getItem('mcap_jwt');
return await mcap.analytics.getUserStats({
userId,
timeRange,
jwt
});
} catch (error) {
console.error('Failed to fetch game stats:', error);
throw error;
}
};

return {
loading,
getTransactionHistory,
getBetHistory,
getGameStats
};
};

Play History Component

// components/PlayHistory.jsx
import React, { useState, useEffect } from 'react';
import { useHistory } from '../hooks/useHistory';
import { ethers } from 'ethers';

const PlayHistory = ({ userId, selectedToken }) => {
const [history, setHistory] = useState([]);
const [filter, setFilter] = useState('all');
const [stats, setStats] = useState(null);
const { loading, getTransactionHistory, getBetHistory, getGameStats } = useHistory();

const fetchHistory = async () => {
try {
const [transactions, bets, userStats] = await Promise.all([
getTransactionHistory({
userId,
tokenId: selectedToken?.id,
limit: 50,
type: filter === 'all' ? undefined : filter
}),
getBetHistory({
userId,
tokenId: selectedToken?.id,
limit: 50,
outcome: filter === 'all' ? undefined : filter
}),
getGameStats(userId, '30d')
]);

// Combine and sort by timestamp
const combined = [
...transactions.map(tx => ({ ...tx, type: 'transaction' })),
...bets.map(bet => ({ ...bet, type: 'bet' }))
].sort((a, b) => new Date(b.created_at) - new Date(a.created_at));

setHistory(combined);
setStats(userStats);

} catch (error) {
console.error('Failed to fetch history:', error);
}
};

useEffect(() => {
if (userId && selectedToken) {
fetchHistory();
}
}, [userId, selectedToken, filter]);

const formatAmount = (amount, decimals) => {
return ethers.utils.formatUnits(amount || '0', decimals);
};

const getStatusColor = (item) => {
if (item.type === 'transaction') {
return item.status === 'completed' ? 'green' : 'orange';
} else {
return item.outcome === 'win' ? 'green' : item.outcome === 'loss' ? 'red' : 'gray';
}
};

return (
<div className="play-history">
{/* User Stats Summary */}
{stats && (
<div className="stats-summary">
<div className="stat-item">
<label>Games Played:</label>
<span>{stats.total_games}</span>
</div>
<div className="stat-item">
<label>Win Rate:</label>
<span>{(stats.win_rate * 100).toFixed(1)}%</span>
</div>
<div className="stat-item">
<label>Total Wagered:</label>
<span>{formatAmount(stats.total_wagered, selectedToken?.decimals)} {selectedToken?.symbol}</span>
</div>
<div className="stat-item">
<label>Net Profit:</label>
<span style={{ color: stats.net_profit >= 0 ? 'green' : 'red' }}>
{formatAmount(stats.net_profit, selectedToken?.decimals)} {selectedToken?.symbol}
</span>
</div>
</div>
)}

{/* History Controls */}
<div className="history-header">
<h3>Activity History</h3>

<div className="history-filters">
<select
value={filter}
onChange={(e) => setFilter(e.target.value)}
>
<option value="all">All Activity</option>
<option value="wins">Wins Only</option>
<option value="losses">Losses Only</option>
<option value="deposits">Deposits</option>
<option value="withdrawals">Withdrawals</option>
</select>

<button onClick={fetchHistory} disabled={loading}>
{loading ? 'Loading...' : 'Refresh'}
</button>
</div>
</div>

{/* History List */}
<div className="history-list">
{history.length === 0 ? (
<div className="no-history">No activity found</div>
) : (
history.map((item, index) => (
<div
key={`${item.type}-${item.id}-${index}`}
className="history-item"
>
<div className="item-info">
<span className="item-type">
{item.type === 'transaction' ?
(item.transaction_type || 'Transaction') :
(item.game_type || 'Game')
}
</span>

<span
className="item-status"
style={{ color: getStatusColor(item) }}
>
{item.type === 'transaction' ? item.status : item.outcome}
</span>
</div>

<div className="item-amount">
{formatAmount(item.amount, selectedToken?.decimals)} {selectedToken?.symbol}
</div>

<div className="item-date">
{new Date(item.created_at).toLocaleDateString()}
</div>

{item.tx_hash && (
<div className="item-tx">
<a
href={`https://flowscan.org/tx/${item.tx_hash}`}
target="_blank"
rel="noopener noreferrer"
>
View Transaction
</a>
</div>
)}
</div>
))
)}
</div>
</div>
);
};

export default PlayHistory;

Advanced SDK Features

Session Management

Explore advanced session handling for secure gaming workflows. This utility class manages session creation, storage, expiration checking, and automatic refresh to maintain uninterrupted gameplay experiences.

// utils/SessionManager.js
import { mcap } from '../mcap-config';

export class SessionManager {
static async createSession(tokenId, gameId) {
try {
const jwt = localStorage.getItem('mcap_jwt');

const sessionResponse = await mcap.sessions.create({
token_id: tokenId,
game_id: gameId
}, jwt);

// Store session locally
const sessionData = {
id: sessionResponse.id,
token_id: tokenId,
game_id: gameId,
created_at: sessionResponse.created_at,
expires_at: sessionResponse.expires_at
};

localStorage.setItem('mcap_session', JSON.stringify(sessionData));

return sessionData;
} catch (error) {
console.error('Session creation failed:', error);
throw error;
}
}

static getStoredSession() {
const sessionStr = localStorage.getItem('mcap_session');
if (!sessionStr) return null;

try {
const session = JSON.parse(sessionStr);

// Check if session is expired
if (new Date(session.expires_at) < new Date()) {
localStorage.removeItem('mcap_session');
return null;
}

return session;
} catch (error) {
localStorage.removeItem('mcap_session');
return null;
}
}

static clearSession() {
localStorage.removeItem('mcap_session');
}

static async refreshSession(sessionId) {
try {
const jwt = localStorage.getItem('mcap_jwt');
return await mcap.sessions.refresh(sessionId, jwt);
} catch (error) {
console.error('Session refresh failed:', error);
throw error;
}
}
}

Error Handling

// utils/ErrorHandler.js
export class MCAPErrorHandler {
static handle(error, userFriendly = true) {
console.error('MCAP Error:', error);

const errorMessages = {
'INSUFFICIENT_BALANCE': 'You don\'t have enough balance for this action.',
'SESSION_EXPIRED': 'Your session has expired. Please reconnect your wallet.',
'INVALID_TOKEN': 'The selected token is not supported.',
'NETWORK_ERROR': 'Network connection error. Please try again.',
'RATE_LIMITED': 'Too many requests. Please wait a moment and try again.',
'BET_TOO_LOW': 'Bet amount is below the minimum required.',
'BET_TOO_HIGH': 'Bet amount exceeds the maximum allowed.',
'WITHDRAWAL_PENDING': 'You have a pending withdrawal. Please wait for it to complete.'
};

if (userFriendly) {
const message = errorMessages[error.code] || error.message || 'An unexpected error occurred.';
return { userMessage: message, originalError: error };
}

return error;
}

static async handleWithRetry(operation, maxRetries = 3) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await operation();
} catch (error) {
if (attempt === maxRetries) {
throw this.handle(error);
}

// Wait before retry (exponential backoff)
const delay = Math.pow(2, attempt) * 1000;
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
}

Environment Configuration

Production Environment

// config/production.js
export const productionConfig = {
baseURL: 'https://mcap-api.mcap.meme',
environment: 'production',
features: {
debugging: false,
testTokens: false,
verboseLogging: false
},
monitoring: {
errorTracking: true,
performanceMonitoring: true,
userAnalytics: true
}
};

Troubleshooting

Common Issues & Solutions

Issue: "Invalid API key" error

Solution: Verify your API key is correct and not being exposed in frontend code

Issue: Wallet connection fails

Solution: Ensure MetaMask is installed and user has approved connection

Issue: Deposit not reflected in balance

Solution: Wait 1-2 blocks for transaction confirmation, then refresh balance

Issue: Session expired errors

Solution: Implement automatic token refresh or prompt user to reconnect

Issue: High gas fees

Solution: Consider batch operations or wait for lower network congestion

Debug Mode

// Enable debug mode for detailed logging
mcap.setDebugMode(true);

// Custom debug logger
mcap.setLogger({
info: (message, data) => console.log(`[MCAP INFO] ${message}`, data),
warn: (message, data) => console.warn(`[MCAP WARN] ${message}`, data),
error: (message, data) => console.error(`[MCAP ERROR] ${message}`, data)
});

Support Resources

Documentation

Community Support


The MCAP SDK provides everything you need to integrate memecoin functionality into your application. Whether you're using existing platform games or building custom experiences, the SDK handles the complexity while providing a simple, powerful interface for your users.