
Security News
npm Adopts OIDC for Trusted Publishing in CI/CD Workflows
npm now supports Trusted Publishing with OIDC, enabling secure package publishing directly from CI/CD workflows without relying on long-lived tokens.
@phantom/browser-sdk
Advanced tools
Browser SDK for Phantom Wallet supporting both injected and embedded non-custodial wallets.
npm install @phantom/browser-sdk
import { BrowserSDK, NetworkId } from "@phantom/browser-sdk";
// Connect to Phantom browser extension
const sdk = new BrowserSDK({
providerType: "injected",
});
const { addresses } = await sdk.connect();
console.log("Connected addresses:", addresses);
// Sign and send transactions
const result = await sdk.signAndSendTransaction({
networkId: NetworkId.SOLANA_MAINNET,
transaction: mySolanaTransaction,
});
import { BrowserSDK, AddressType, NetworkId } from "@phantom/browser-sdk";
// Create embedded non-custodial wallet
const sdk = new BrowserSDK({
providerType: "embedded",
addressTypes: [AddressType.solana, AddressType.ethereum],
apiBaseUrl: "https://api.phantom.com",
organizationId: "your-org-id",
});
const { walletId, addresses } = await sdk.connect();
console.log("Wallet ID:", walletId);
console.log("Addresses:", addresses);
const result = await sdk.signAndSendTransaction({
networkId: NetworkId.SOLANA_MAINNET,
transaction: mySolanaTransaction,
});
Uses the Phantom browser extension installed by the user. No additional configuration needed.
const sdk = new BrowserSDK({
providerType: "injected",
});
Creates a non-custodial wallet embedded in your application. Requires API configuration.
const sdk = new BrowserSDK({
providerType: "embedded",
addressTypes: [AddressType.solana, AddressType.ethereum],
apiBaseUrl: "https://api.phantom.com",
organizationId: "your-org-id",
embeddedWalletType: "app-wallet", // or 'user-wallet'
authOptions: {
authUrl: "https://auth.phantom.app", // optional, defaults to "https://connect.phantom.app"
redirectUrl: "https://yourapp.com/callback", // optional, defaults to current page
},
});
'app-wallet'
)const sdk = new BrowserSDK({
providerType: "embedded",
embeddedWalletType: "app-wallet",
addressTypes: [AddressType.solana],
// ... other config
});
'user-wallet'
)const sdk = new BrowserSDK({
providerType: "embedded",
embeddedWalletType: "user-wallet",
addressTypes: [AddressType.solana, AddressType.ethereum],
// ... other config
});
AddressType | Networks Supported |
---|---|
AddressType.solana | Solana Mainnet, Devnet, Testnet |
AddressType.ethereum | Ethereum, Polygon, Arbitrum, Optimism, Base |
AddressType.bitcoinSegwit | Bitcoin Mainnet, Testnet |
AddressType.sui | Sui Mainnet, Testnet, Devnet |
When using AddressType.solana
, you can choose between two Solana libraries:
const sdk = new BrowserSDK({
providerType: "embedded",
addressTypes: [AddressType.solana],
solanaProvider: "web3js", // or 'kit'
// ... other config
});
Provider Options:
'web3js'
(default) - Uses @solana/web3.js
library'kit'
- Uses @solana/kit
library (modern, TypeScript-first)When to use each:
new BrowserSDK(config: BrowserSDKConfig)
interface BrowserSDKConfig {
providerType: "injected" | "embedded";
// Required for embedded provider only
addressTypes?: AddressType[]; // Networks to enable
apiBaseUrl?: string; // Phantom API base URL
organizationId?: string; // Your organization ID
authOptions?: {
authUrl?: string; // Custom auth URL (default: "https://connect.phantom.app")
redirectUrl?: string; // Custom redirect URL after authentication
};
embeddedWalletType?: "app-wallet" | "user-wallet"; // Wallet type
solanaProvider?: "web3js" | "kit"; // Solana library choice (default: 'web3js')
}
Connect to wallet and get addresses for configured AddressTypes.
const result = await sdk.connect();
// Returns: { walletId: string, addresses: WalletAddress[] }
// addresses only includes types from addressTypes config
For embedded user-wallets, you can specify authentication options:
// Phantom Connect with provider selection (default)
const result = await sdk.connect();
// Phantom Connect with Google authentication (skips provider selection)
const result = await sdk.connect({
authOptions: {
provider: "google",
},
});
// Phantom Connect with Apple authentication (skips provider selection)
const result = await sdk.connect({
authOptions: {
provider: "apple",
},
});
// JWT authentication (direct API call)
const result = await sdk.connect({
authOptions: {
provider: "jwt",
jwtToken: "your-jwt-token",
customAuthData: { userId: "user123" },
},
});
Authentication Options:
provider
- Authentication method: "google"
, "apple"
, or "jwt"
"google"
or "apple"
: Skips provider selection and uses specified provider"jwt"
: Uses JWT authentication flow via API calljwtToken
- Required when provider
is "jwt"
. Your JWT token for authenticationcustomAuthData
- Additional data to pass to authentication serviceAuthentication Flow Types:
Phantom Connect (Redirect-based): Used when provider
is undefined, "google"
, or "apple"
https://connect.phantom.app
(or custom authOptions.authUrl
from config)authOptions.redirectUrl
or current pageJWT Authentication (API-based): Used when provider
is "jwt"
/api/auth/jwt
endpointSign and send a native transaction object.
// Solana transaction
const result = await sdk.signAndSendTransaction({
networkId: NetworkId.SOLANA_MAINNET,
transaction: solanaTransaction, // Native Transaction or VersionedTransaction
});
// Ethereum transaction
const result = await sdk.signAndSendTransaction({
networkId: NetworkId.ETHEREUM_MAINNET,
transaction: {
to: "0x...",
value: parseEther("1"), // 1 ETH
gas: 21000n,
},
});
Sign a message string.
const signature = await sdk.signMessage({
message: "Hello from Phantom!",
networkId: NetworkId.SOLANA_MAINNET,
});
Parameters:
params.message
(string) - Message to signparams.networkId
(NetworkId) - Network identifierGet connected wallet addresses.
const addresses = await sdk.getAddresses();
// Returns addresses matching configured AddressTypes
Disconnect from wallet and clear session.
await sdk.disconnect();
The SDK supports two different Solana transaction libraries. Choose based on your needs:
Traditional Solana library with broader ecosystem support.
npm install @solana/web3.js
import { Transaction, SystemProgram, PublicKey, LAMPORTS_PER_SOL } from "@solana/web3.js";
import { BrowserSDK, NetworkId } from "@phantom/browser-sdk";
// Create native Solana transaction
const transaction = new Transaction().add(
SystemProgram.transfer({
fromPubkey: new PublicKey(fromAddress),
toPubkey: new PublicKey(toAddress),
lamports: 0.001 * LAMPORTS_PER_SOL,
}),
);
// Send native transaction object - no encoding needed!
const result = await sdk.signAndSendTransaction({
networkId: NetworkId.SOLANA_MAINNET,
transaction: transaction,
});
console.log("Transaction signature:", result.rawTransaction);
VersionedTransaction with @solana/web3.js:
import { VersionedTransaction } from "@solana/web3.js";
const versionedTx = new VersionedTransaction(message);
const result = await sdk.signAndSendTransaction({
networkId: NetworkId.SOLANA_DEVNET,
transaction: versionedTx,
});
New high-performance Solana library with better TypeScript support.
npm install @solana/kit
import {
createSolanaRpc,
pipe,
createTransactionMessage,
setTransactionMessageFeePayer,
setTransactionMessageLifetimeUsingBlockhash,
address,
compileTransaction,
} from "@solana/kit";
import { BrowserSDK, NetworkId } from "@phantom/browser-sdk";
// Create transaction with @solana/kit
const rpc = createSolanaRpc("https://api.mainnet-beta.solana.com");
const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();
const transactionMessage = pipe(
createTransactionMessage({ version: 0 }),
tx => setTransactionMessageFeePayer(address(userPublicKey), tx),
tx => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx),
);
const transaction = compileTransaction(transactionMessage);
const result = await sdk.signAndSendTransaction({
networkId: NetworkId.SOLANA_MAINNET,
transaction: transaction,
});
import { parseEther, parseGwei, encodeFunctionData } from "viem";
import { BrowserSDK, NetworkId } from "@phantom/browser-sdk";
// Simple ETH transfer
const result = await sdk.signAndSendTransaction({
networkId: NetworkId.ETHEREUM_MAINNET,
transaction: {
to: "0x742d35Cc6634C0532925a3b8D4C8db86fB5C4A7E",
value: parseEther("1"), // 1 ETH
gas: 21000n,
gasPrice: parseGwei("20"), // 20 gwei
},
});
// EIP-1559 transaction with maxFeePerGas
const result = await sdk.signAndSendTransaction({
networkId: NetworkId.ETHEREUM_MAINNET,
transaction: {
to: "0x742d35Cc6634C0532925a3b8D4C8db86fB5C4A7E",
value: parseEther("1"),
data: encodeFunctionData({
abi: tokenAbi,
functionName: "transfer",
args: ["0x742d35Cc6634C0532925a3b8D4C8db86fB5C4A7E", parseEther("1")],
}),
gas: 50000n,
maxFeePerGas: parseGwei("30"), // 30 gwei
maxPriorityFeePerGas: parseGwei("2"), // 2 gwei
type: "eip1559",
},
});
// Polygon transaction
const result = await sdk.signAndSendTransaction({
networkId: NetworkId.POLYGON_MAINNET,
transaction: {
to: "0x742d35Cc6634C0532925a3b8D4C8db86fB5C4A7E",
value: parseEther("1"), // 1 MATIC
gas: 21000n,
},
});
// Arbitrum transaction
const result = await sdk.signAndSendTransaction({
networkId: NetworkId.ARBITRUM_ONE,
transaction: {
to: "0x742d35Cc6634C0532925a3b8D4C8db86fB5C4A7E",
value: parseEther("0.1"), // 0.1 ETH
gas: 21000n,
},
});
// Bitcoin transaction
const result = await sdk.signAndSendTransaction({
networkId: NetworkId.BITCOIN_MAINNET,
transaction: {
inputs: [
{
txid: "previous-transaction-id",
vout: 0,
scriptSig: "...",
},
],
outputs: [
{
value: 50000, // satoshis
scriptPubKey: "76a914...88ac", // P2PKH script
},
],
version: 2,
locktime: 0,
},
});
// Bitcoin testnet
const result = await sdk.signAndSendTransaction({
networkId: NetworkId.BITCOIN_TESTNET,
transaction: {
// ... transaction details
},
});
import { TransactionBlock } from "@mysten/sui.js/transactions";
// Create Sui transaction block
const txb = new TransactionBlock();
txb.transferObjects([coin], recipientAddress);
const result = await sdk.signAndSendTransaction({
networkId: NetworkId.SUI_MAINNET,
transaction: {
kind: "moveCall", // or 'transferObject', 'transferSui', 'pay'
data: txb, // TransactionBlock from @mysten/sui.js
},
});
// Sui testnet
const result = await sdk.signAndSendTransaction({
networkId: NetworkId.SUI_TESTNET,
transaction: {
kind: "transferSui",
data: suiTransactionData,
},
});
Use the exported NetworkId
enum for type safety:
import { NetworkId } from "@phantom/browser-sdk";
NetworkId.SOLANA_MAINNET
- Solana Mainnet BetaNetworkId.SOLANA_DEVNET
- Solana DevnetNetworkId.SOLANA_TESTNET
- Solana TestnetNetworkId.ETHEREUM_MAINNET
- Ethereum MainnetNetworkId.ETHEREUM_SEPOLIA
- Ethereum Sepolia TestnetNetworkId.POLYGON_MAINNET
- Polygon MainnetNetworkId.ARBITRUM_ONE
- Arbitrum OneNetworkId.OPTIMISM_MAINNET
- Optimism MainnetNetworkId.BASE_MAINNET
- Base MainnetNetworkId.BITCOIN_MAINNET
- Bitcoin MainnetNetworkId.BITCOIN_TESTNET
- Bitcoin TestnetNetworkId.SUI_MAINNET
- Sui MainnetNetworkId.SUI_TESTNET
- Sui TestnetNetworkId.SUI_DEVNET
- Sui Devnetimport { BrowserSDK, AddressType } from "@phantom/browser-sdk";
const sdk = new BrowserSDK({
addressTypes: [AddressType.solana, AddressType.ethereum, AddressType.sui],
apiBaseUrl: "https://api.phantom.com",
organizationId: "your-org-id",
});
class MultiChainWallet {
async sendSolana(amount: number, recipient: string) {
const transaction = new Transaction().add(
SystemProgram.transfer({
fromPubkey: new PublicKey(this.solanaAddress),
toPubkey: new PublicKey(recipient),
lamports: amount * LAMPORTS_PER_SOL,
}),
);
return await sdk.signAndSendTransaction({
networkId: NetworkId.SOLANA_MAINNET,
transaction,
});
}
async sendEthereum(amount: string, recipient: string) {
return await sdk.signAndSendTransaction({
networkId: NetworkId.ETHEREUM_MAINNET,
transaction: {
to: recipient,
value: parseEther(amount),
gas: 21000n,
},
});
}
async sendSui(coinId: string, recipient: string) {
const txb = new TransactionBlock();
txb.transferObjects([coinId], recipient);
return await sdk.signAndSendTransaction({
networkId: NetworkId.SUI_MAINNET,
transaction: {
kind: "transferObject",
data: txb,
},
});
}
}
try {
const result = await sdk.signAndSendTransaction({
networkId: NetworkId.SOLANA_MAINNET,
transaction: myTransaction,
});
console.log("Success:", result);
} catch (error) {
if (error.message.includes("User rejected")) {
console.log("User cancelled the transaction");
} else if (error.message.includes("insufficient funds")) {
console.log("Not enough balance");
} else {
console.error("Transaction failed:", error);
}
}
Only include networks you need:
// Good: Only Solana (~250KB)
addressTypes: [AddressType.solana];
// Avoid: All networks if not needed (~800KB+)
addressTypes: [AddressType.solana, AddressType.ethereum, AddressType.sui, AddressType.bitcoinSegwit];
Install dependencies based on enabled networks:
AddressType | Required Dependencies | Bundle Size |
---|---|---|
AddressType.solana | @solana/web3.js OR @solana/kit | ~250KB |
AddressType.ethereum | viem | ~300KB |
AddressType.bitcoinSegwit | bitcoinjs-lib | ~200KB |
AddressType.sui | @mysten/sui.js | ~250KB |
Example package.json for Solana + Ethereum (using @solana/web3.js):
{
"dependencies": {
"@phantom/browser-sdk": "^1.0.0",
"@solana/web3.js": "^1.87.0",
"viem": "^2.0.0"
}
}
Example package.json for Solana + Ethereum (using @solana/kit):
{
"dependencies": {
"@phantom/browser-sdk": "^1.0.0",
"@solana/kit": "^2.0.0",
"viem": "^2.0.0"
}
}
Example package.json for Solana only (using @solana/web3.js):
{
"dependencies": {
"@phantom/browser-sdk": "^1.0.0",
"@solana/web3.js": "^1.87.0"
}
}
Example package.json for Solana only (using @solana/kit):
{
"dependencies": {
"@phantom/browser-sdk": "^1.0.0",
"@solana/kit": "^2.0.0"
}
}
Example package.json for all networks (using @solana/web3.js):
{
"dependencies": {
"@phantom/browser-sdk": "^1.0.0",
"@solana/web3.js": "^1.87.0",
"viem": "^2.0.0",
"bitcoinjs-lib": "^6.1.0",
"@mysten/sui.js": "^0.50.0"
}
}
Example package.json for all networks (using @solana/kit):
{
"dependencies": {
"@phantom/browser-sdk": "^1.0.0",
"@solana/kit": "^2.0.0",
"viem": "^2.0.0",
"bitcoinjs-lib": "^6.1.0",
"@mysten/sui.js": "^0.50.0"
}
}
Monitor bundle size:
# Analyze your bundle
npx webpack-bundle-analyzer dist/main.js
For embedded wallets, you need to set up a backend endpoint. Add the serverUrl
parameter to your SDK configuration:
const sdk = new BrowserSDK({
providerType: "embedded",
addressTypes: [AddressType.solana],
apiBaseUrl: "https://api.phantom.com",
organizationId: "your-org-id",
serverUrl: "http://localhost:3000/api",
});
Your backend needs an endpoint that uses the server-sdk:
// server.js
const express = require("express");
const { ServerSDK } = require("@phantom/server-sdk");
const app = express();
app.use(express.json());
const serverSDK = new ServerSDK({
organizationId: process.env.ORGANIZATION_ID,
apiPrivateKey: process.env.PRIVATE_KEY,
apiBaseUrl: process.env.API_URL,
});
app.post("/api/organizations", async (req, res) => {
try {
const { userId } = req.body;
if (!userId) {
return res.status(400).json({ error: "userId is required" });
}
const organization = await serverSDK.getOrCreateChildOrganizationByTag({
tag: userId,
});
res.json({ organizationId: organization.id });
} catch (error) {
res.status(500).json({ error: "Failed to process request" });
}
});
app.listen(3000);
FAQs
Browser SDK for Phantom Wallet with unified interface
The npm package @phantom/browser-sdk receives a total of 1,603 weekly downloads. As such, @phantom/browser-sdk popularity was classified as popular.
We found that @phantom/browser-sdk demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 3 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
npm now supports Trusted Publishing with OIDC, enabling secure package publishing directly from CI/CD workflows without relying on long-lived tokens.
Research
/Security News
A RubyGems malware campaign used 60 malicious packages posing as automation tools to steal credentials from social media and marketing tool users.
Security News
The CNA Scorecard ranks CVE issuers by data completeness, revealing major gaps in patch info and software identifiers across thousands of vulnerabilities.