Wallet Integration
Connecting wallets to Selendra applications
Wallet integration connects users to blockchain applications. Multiple approaches based on target audience and technical requirements. Cambodia market considerations influence strategy.
Wallet Comparison
| Wallet Type | User Experience | Security | Best For |
|---|---|---|---|
| MetaMask | Familiar to crypto users | Self-custody | EVM contracts, experienced users |
| Polkadot.js | Substrate-native | Self-custody | WASM contracts, full features |
| Custodial | Simplified, no seed phrases | Backend-managed | Mainstream users, onboarding |
| Social Login | Email/phone authentication | Custodial with 2FA | Cambodia market, beginners |
MetaMask Integration
Adding Selendra Network
async function addSelendraNetwork() {
try {
await window.ethereum.request({
method: "wallet_addEthereumChain",
params: [
{
chainId: "0x1F6",
chainName: "Selendra Network",
nativeCurrency: {
name: "Selendra",
symbol: "SEL",
decimals: 18,
},
rpcUrls: ["https://rpc.selendra.org"],
blockExplorerUrls: ["https://explorer.selendra.org"],
},
],
});
} catch (error) {
console.error("Failed to add network:", error);
}
}
Connection Flow
async function connectMetaMask() {
if (typeof window.ethereum === "undefined") {
alert("Please install MetaMask");
return;
}
try {
const accounts = await window.ethereum.request({
method: "eth_requestAccounts",
});
const chainId = await window.ethereum.request({
method: "eth_chainId",
});
if (chainId !== "0x1F6") {
await window.ethereum.request({
method: "wallet_switchEthereumChain",
params: [{ chainId: "0x1F6" }],
});
}
return accounts[0];
} catch (error) {
console.error("Connection failed:", error);
throw error;
}
}
Network and Account Listeners
// Network changes
window.ethereum.on("chainChanged", (chainId) => {
if (chainId !== "0x1F6") {
switchToSelendra();
} else {
window.location.reload();
}
});
// Account changes
window.ethereum.on("accountsChanged", (accounts) => {
if (accounts.length === 0) {
handleDisconnect();
} else {
handleAccountChange(accounts[0]);
}
});
Mobile MetaMask
const dappUrl = "https://yourdapp.com";
const metamaskAppDeepLink = `https://metamask.app.link/dapp/${dappUrl}`;
function isMetaMaskBrowser() {
return (
window.ethereum?.isMetaMask &&
window.navigator.userAgent.includes("MetaMask")
);
}
function isMobile() {
return /Android|webOS|iPhone|iPad|iPod/i.test(navigator.userAgent);
}
// Redirect mobile users to MetaMask browser
if (isMobile && !isMetaMaskBrowser) {
window.location.href = metamaskAppDeepLink;
}
Polkadot.js Extension
Connection
import {
web3Accounts,
web3Enable,
web3FromAddress,
} from "@polkadot/extension-dapp";
async function connectPolkadotWallet(appName) {
const extensions = await web3Enable(appName);
if (extensions.length === 0) {
throw new Error("Polkadot.js extension not installed");
}
const accounts = await web3Accounts();
if (accounts.length === 0) {
throw new Error("No accounts available");
}
return accounts;
}
Signing Transactions
async function signAndSendTransaction(accountAddress, extrinsic) {
const injector = await web3FromAddress(accountAddress);
const hash = await extrinsic.signAndSend(accountAddress, {
signer: injector.signer,
});
return hash;
}
Account Selection
function AccountSelector({ accounts, onSelect }) {
return (
<select onChange={(e) => onSelect(e.target.value)}>
{accounts.map((account) => (
<option key={account.address} value={account.address}>
{account.meta.name || account.address.slice(0, 8)}
</option>
))}
</select>
);
}
Custodial Wallet Implementation
Architecture
Backend manages keys. Frontend handles authentication and transaction approval. Suitable for mainstream users.
Security requirements:
- Hardware Security Module (HSM) for key storage
- Multi-signature withdrawal approval
- Rate limiting per user
- Daily withdrawal limits
- Audit logging
Social Login
import { GoogleAuthProvider, signInWithPopup } from "firebase/auth";
async function loginWithGoogle() {
const provider = new GoogleAuthProvider();
const result = await signInWithPopup(auth, provider);
// Backend creates/retrieves wallet
const response = await fetch("/api/wallet/create", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
userId: result.user.uid,
email: result.user.email,
}),
});
const { walletAddress } = await response.json();
return walletAddress;
}
Transaction Approval
Frontend request:
async function requestTransaction(action, params) {
const response = await fetch("/api/transaction/request", {
method: "POST",
headers: {
Authorization: `Bearer ${authToken}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ action, params }),
});
const { transactionId, estimatedCost } = await response.json();
const approved = await showConfirmationDialog(action, estimatedCost);
if (approved) {
return await executeTransaction(transactionId);
}
}
Backend execution:
async function executeTransaction(transactionId, userWallet) {
const privateKey = await getPrivateKey(userWallet); // From HSM
const wallet = new ethers.Wallet(privateKey, provider);
const tx = await getPendingTransaction(transactionId);
const receipt = await wallet.sendTransaction(tx);
await logTransaction(transactionId, receipt.hash);
return receipt;
}
StadiumX Implementation
Hybrid approach: MetaMask for experienced users, custodial for new users.
Onboarding:
- Social login creates account
- Backend generates wallet automatically
- User receives NFT ticket immediately
- Option to export key later (advanced)
Security:
- Rate limit: 10 transactions/user/day
- Max transaction: $10 equivalent
- SMS verification for withdrawals
- Insurance fund
Multi-Wallet Support
Detection and Fallback
async function connectWallet() {
// MetaMask
if (window.ethereum?.isMetaMask) {
return await connectMetaMask();
}
// Polkadot.js
const extensions = await web3Enable("YourApp");
if (extensions.length > 0) {
return await connectPolkadotWallet();
}
// Custodial fallback
return await showSocialLoginOptions();
}
Unified Interface
class WalletAdapter {
constructor(type, provider) {
this.type = type;
this.provider = provider;
}
async getAddress() {
switch (this.type) {
case "metamask":
return (
await this.provider.request({
method: "eth_accounts",
})
)[0];
case "polkadot":
return (await web3Accounts())[0].address;
case "custodial":
return this.provider.walletAddress;
}
}
async signTransaction(tx) {
// Implement per wallet type
}
}
Cambodia Market Considerations
Mobile-first: Desktop usage minimal. Optimize for small screens. MetaMask mobile browser critical.
Low data usage: Internet data expensive. Minimize API calls. Cache aggressively. Optimize bundle size.
Language: Khmer option increases accessibility. English OK for technical terms. Visual indicators reduce text dependency.
Support: Telegram most popular in Cambodia. Provide Telegram bot/group support. Phone support for older users.
Related Documentation
Transaction Handling | Event Monitoring | EVM Smart Contracts
Community
Telegram: t.me/selendranetwork | X: @selendranetwork | GitHub: github.com/selendra/selendra
Found an issue or want to contribute?
Help us improve this documentation by editing this page on GitHub.
