You're Invited:Meet the Socket Team at BlackHat and DEF CON in Las Vegas, Aug 4-6.RSVP
Socket
Book a DemoInstallSign in
Socket

@phantom/browser-sdk

Package Overview
Dependencies
Maintainers
3
Versions
8
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@phantom/browser-sdk - npm Package Compare versions

Comparing version

to
0.0.4

73

dist/solana/index.d.ts
import { ChainPlugin } from '../index.js';
import { PublicKey, Transaction, VersionedTransaction, SendOptions } from '@solana/web3.js';
import { Transaction as Transaction$1 } from '@solana/kit';
type Transaction = {
message: Uint8Array;
recentBlockhash: string;
feePayer: string;
instructions: any[];
signers: string[];
version: number;
};
type VersionedTransaction = {
signatures: Uint8Array[];
message: {
deserialize: (serializedTransaction: Uint8Array) => VersionedTransaction;
serialize: (transaction: VersionedTransaction) => Uint8Array;
};
};
type SendOptions = {
skipPreflight?: boolean;
preflightCommitment?: string;
maxRetries?: number;
minContextSlot?: number;
};
type PublicKey = {
toString: () => string;
toBase58: () => string;
};
type SolanaSignInData = {

@@ -19,2 +44,3 @@ domain?: string;

type DisplayEncoding = "utf8" | "hex";
type PhantomEventType = "connect" | "disconnect" | "accountChanged";
interface PhantomSolanaProvider {

@@ -45,6 +71,5 @@ isPhantom: boolean;

signTransaction: (transaction: Transaction | VersionedTransaction) => Promise<Transaction | VersionedTransaction>;
on: (event: "connect" | "disconnect" | "accountChanged", handler: (publicKey?: PublicKey) => void) => void;
off: (event: "connect" | "disconnect" | "accountChanged", handler: (publicKey?: PublicKey) => void) => void;
}
interface SolanaOperationOptions {
getProvider?: () => PhantomSolanaProvider | null;
}

@@ -55,9 +80,8 @@ /**

* @param display The display encoding for the message (optional, defaults to utf8).
* @param options Optional parameters, including a custom getProvider function.
* @returns A promise that resolves with the signature and public key.
* @throws Error if Phantom provider is not found or if the operation fails.
*/
declare function signMessage(message: Uint8Array, display?: DisplayEncoding, options?: SolanaOperationOptions): Promise<{
declare function signMessage(message: Uint8Array, display?: DisplayEncoding): Promise<{
signature: Uint8Array;
publicKey: PublicKey;
address: string;
}>;

@@ -68,8 +92,7 @@

* @param signInData The sign-in data.
* @param options Optional parameters, including a custom getProvider function.
* @returns A promise that resolves with the address, signature, and signed message.
* @throws Error if Phantom provider is not found or if the operation fails.
*/
declare function signIn(signInData: SolanaSignInData, options?: SolanaOperationOptions): Promise<{
address: PublicKey;
declare function signIn(signInData: SolanaSignInData): Promise<{
address: string;
signature: Uint8Array;

@@ -82,16 +105,38 @@ signedMessage: Uint8Array;

* @param transaction The transaction to sign and send.
* @param options Options for sending the transaction and other operations.
* @returns A promise that resolves with the transaction signature and optionally the public key.
* @throws Error if Phantom provider is not found or if the operation fails.
*/
declare function signAndSendTransaction(transaction: Transaction | VersionedTransaction, operationOptions?: SolanaOperationOptions): Promise<{
declare function signAndSendTransaction(transaction: Transaction$1): Promise<{
signature: string;
publicKey?: string;
address?: string;
}>;
declare function connect(): Promise<string | undefined>;
declare function disconnect(): Promise<void>;
type ConnectCallback = (publicKey: string) => void;
type DisconnectCallback = () => void;
type AccountChangedCallback = (publicKey: string) => void;
type PhantomEventCallback = ConnectCallback | DisconnectCallback | AccountChangedCallback;
type GetAccountResult = {
status: "connected";
address: string;
} | {
status: "disconnected";
address: null;
};
declare function getAccount(): GetAccountResult;
type Solana = {
getProvider: () => PhantomSolanaProvider | null;
connect: typeof connect;
disconnect: typeof disconnect;
getAccount: typeof getAccount;
signMessage: typeof signMessage;
signIn: typeof signIn;
signAndSendTransaction: typeof signAndSendTransaction;
addEventListener: (event: PhantomEventType, callback: PhantomEventCallback) => () => void;
removeEventListener: (event: PhantomEventType, callback: PhantomEventCallback) => void;
};

@@ -106,2 +151,2 @@ declare function createSolanaPlugin(): ChainPlugin<Solana>;

export { createSolanaPlugin };
export { PhantomSolanaProvider, SolanaSignInData, createSolanaPlugin };

@@ -32,9 +32,74 @@ "use strict";

// src/solana/eventListeners.ts
var eventCallbacks = /* @__PURE__ */ new Map();
function addEventListener(event, callback) {
if (!eventCallbacks.has(event)) {
eventCallbacks.set(event, /* @__PURE__ */ new Set());
}
eventCallbacks.get(event).add(callback);
return () => {
removeEventListener(event, callback);
};
}
function removeEventListener(event, callback) {
if (eventCallbacks.has(event)) {
eventCallbacks.get(event).delete(callback);
if (eventCallbacks.get(event).size === 0) {
eventCallbacks.delete(event);
}
}
}
function triggerEvent(event, ...args) {
if (eventCallbacks.has(event)) {
eventCallbacks.get(event).forEach((cb) => {
if (event === "connect" && args[0] && typeof args[0] === "string") {
cb(args[0]);
} else if (event === "disconnect") {
cb();
} else if (event === "accountChanged" && args[0] && typeof args[0] === "string") {
cb(args[0]);
}
});
}
}
// src/solana/connect.ts
async function connect() {
const provider = getProvider();
if (!provider) {
throw new Error("Phantom provider not found.");
}
if (provider.isConnected) {
return provider.publicKey?.toString();
}
try {
const eagerConnectResult = await provider.connect({ onlyIfTrusted: true });
if (eagerConnectResult.publicKey) {
const publicKeyStr = eagerConnectResult.publicKey.toString();
triggerEvent("connect", publicKeyStr);
return publicKeyStr;
}
} catch (error) {
}
try {
const connectResult = await provider.connect({ onlyIfTrusted: false });
if (connectResult.publicKey) {
const publicKeyStr = connectResult.publicKey.toString();
triggerEvent("connect", publicKeyStr);
return publicKeyStr;
}
} catch (error) {
}
throw new Error("Failed to connect to Phantom.");
}
// src/solana/signMessage.ts
async function signMessage(message, display, options) {
const getProviderFn = options?.getProvider || getProvider;
const provider = getProviderFn();
async function signMessage(message, display) {
const provider = getProvider();
if (!provider) {
throw new Error("Phantom provider not found.");
}
if (!provider.isConnected) {
await connect();
}
if (!provider.signMessage) {

@@ -44,11 +109,14 @@ throw new Error("The connected provider does not support signMessage.");

if (!provider.isConnected) {
throw new Error("Provider is not connected.");
throw new Error("Provider is not connected even after attempting to connect.");
}
return provider.signMessage(message, display);
const result = await provider.signMessage(message, display);
return {
signature: result.signature,
address: result.publicKey.toString()
};
}
// src/solana/signIn.ts
async function signIn(signInData, options) {
const getProviderFn = options?.getProvider || getProvider;
const provider = getProviderFn();
async function signIn(signInData) {
const provider = getProvider();
if (!provider) {

@@ -60,12 +128,31 @@ throw new Error("Phantom provider not found.");

}
return provider.signIn(signInData);
const result = await provider.signIn(signInData);
return {
address: result.address.toString(),
signature: result.signature,
signedMessage: result.signedMessage
};
}
// src/solana/utils/transactionToVersionedTransaction.ts
var import_transactions = require("@solana/transactions");
function transactionToVersionedTransaction(transaction) {
const serialized = (0, import_transactions.getTransactionEncoder)().encode(transaction);
const fakeVersioned = {
serialize() {
return new Uint8Array(serialized);
}
};
return fakeVersioned;
}
// src/solana/signAndSendTransaction.ts
async function signAndSendTransaction(transaction, operationOptions) {
const getProviderFn = operationOptions?.getProvider || getProvider;
const provider = getProviderFn();
async function signAndSendTransaction(transaction) {
const provider = getProvider();
if (!provider) {
throw new Error("Phantom provider not found.");
}
if (!provider.isConnected) {
await connect();
}
if (!provider.signAndSendTransaction) {

@@ -75,13 +162,48 @@ throw new Error("The connected provider does not support signAndSendTransaction.");

if (!provider.isConnected) {
throw new Error("Provider is not connected.");
throw new Error("Provider is not connected even after attempting to connect.");
}
return provider.signAndSendTransaction(transaction);
const versionedTransaction = transactionToVersionedTransaction(transaction);
const result = await provider.signAndSendTransaction(versionedTransaction);
return {
signature: result.signature,
address: result.publicKey
};
}
// src/solana/disconnect.ts
async function disconnect() {
const provider = getProvider();
if (!provider) {
throw new Error("Phantom provider not found.");
}
await provider.disconnect();
triggerEvent("disconnect");
}
// src/solana/getAccount.ts
function getAccount() {
const provider = getProvider();
if (provider && provider.isConnected && provider.publicKey) {
return {
status: "connected",
address: provider.publicKey.toString()
};
}
return {
status: "disconnected",
address: null
};
}
// src/solana/plugin.ts
var solana = {
getProvider,
connect,
disconnect,
getAccount,
signMessage,
signIn,
signAndSendTransaction
signAndSendTransaction,
addEventListener,
removeEventListener
};

@@ -88,0 +210,0 @@ function createSolanaPlugin() {

8

package.json
{
"name": "@phantom/browser-sdk",
"version": "0.0.3",
"version": "0.0.4",
"main": "dist/index.js",

@@ -25,2 +25,3 @@ "module": "dist/index.mjs",

"build": "rm -rf dist && tsup src/index.ts src/solana/index.ts --format cjs,esm --dts",
"build:watch": "rm -rf dist && tsup src/index.ts src/solana/index.ts --format cjs,esm --dts --watch",
"dev": "rm -rf dist && tsup src/index.ts src/solana/index.ts --format cjs,esm --dts --watch",

@@ -31,2 +32,3 @@ "lint": "tsc --noEmit && eslint --cache . --ext .ts,.tsx",

"devDependencies": {
"@solana/web3.js": "^1.98.2",
"eslint": "8.53.0",

@@ -37,4 +39,6 @@ "tsup": "^6.7.0",

"dependencies": {
"@solana/web3.js": "^1.98.2"
"@solana/compat": "2.1.1",
"@solana/kit": "^2.1.1",
"@solana/transactions": "^2.1.1"
}
}
# Phantom Browser SDK
WIP
The Phantom Browser SDK allows you to interact with the Phantom wallet from your web application. It uses a plugin system to support different blockchains.
## Installation
You can install the SDK using npm or yarn:
**npm:**
```bash
npm install @phantom/browser-sdk
```
**yarn:**
```bash
yarn add @phantom/browser-sdk
```
## Usage
Here's an example of how to import and use the SDK with the Solana plugin:
```typescript
import { createPhantom } from "@phantom/browser-sdk";
import { createSolanaPlugin } from "@phantom/browser-sdk/solana"; // Import the solana plugin
// Create a Phantom instance with the Solana plugin
const phantom = createPhantom({
chainPlugins: [createSolanaPlugin()],
});
// Now you can use the Solana-specific methods
async function connectAndSign() {
try {
// Get the Solana provider (Phantom wallet instance)
const provider = phantom.solana.getProvider();
if (!provider) {
console.error("Phantom wallet not found. Please install Phantom.");
return;
}
// Attempt to connect to the wallet
const connectionResult = await phantom.solana.connect();
console.log("Connection Result:", connectionResult.address);
// Example: Sign in (if supported by the specific provider/plugin)
// Construct SolanaSignInData according to your needs
const signInData = { domain: window.location.host, statement: "Please sign in to access this dApp." };
const signInResult = await phantom.solana.signIn(signInData);
console.log("Sign In Result:", signInResult.address);
// Example: Sign a message
const message = new TextEncoder().encode("Hello from Phantom Browser SDK!");
const signedMessage = await phantom.solana.signMessage(message, "utf8");
console.log("Signed Message:", signedMessage);
} catch (error) {
console.error("Error interacting with Phantom:", error);
}
}
connectAndSign();
```
### Available Solana Methods
Once the `phantom.solana` object is initialized, you can access the following methods:
- `getProvider(): PhantomSolanaProvider | null`
- Retrieves the Phantom Solana provider instance.
- `connect(opts?: { onlyIfTrusted?: boolean }): Promise<string>`
- Connects to the Phantom wallet. Optionally, `onlyIfTrusted` can be set to true to only connect if the dApp is already trusted.
- `disconnect(): Promise<void>`
- Disconnects from the Phantom wallet.
- `getAccount(): { status: "connected" | "disconnected"; address: string | null }`
- Gets the current connected account state. When account is connected returns a public key, when it's not returns it as null.
- `signIn(): Promise<SignInResult>`
- Initiates a sign-in request to the wallet.
- `signMessage(message: Uint8Array | string, display?: 'utf8' | 'hex'): Promise<SignedMessage>`
- Prompts the user to sign a given message.
- `signAndSendTransaction(transaction: Transaction): Promise<{ signature: string; address?: string }>`
- Prompts the user to sign **and send** a Kit `Transaction` and returns the confirmed signature.
### Event Handling
The SDK also allows you to listen for `connect`, `disconnect`, and `accountChanged` events:
- `addEventListener(event: PhantomEventType, callback: PhantomEventCallback): () => void`
- Registers a callback that will be invoked when the specified event occurs.
- For the `connect` event, the callback receives the public key (as a string) of the connected account.
- For the `disconnect` event, the callback receives no arguments.
- For the `accountChanged` event, the callback receives the new public key (as a string).
- Returns a function that, when called, will unregister the callback.
- Multiple callbacks can be registered for the same event.
**Example:**
```typescript
const phantom = createPhantom({ chainPlugins: [createSolanaPlugin()] });
const handleConnect = (address: string) => {
console.log(`Wallet connected with public key: ${address}`);
};
const clearConnectListener = phantom.solana.addEventListener("connect", handleConnect);
const handleAccountChanged = (newAddress: string) => {
console.log(`Account changed to: ${newAddress}`);
};
const clearAccountChangedListener = phantom.solana.addEventListener("accountChanged", handleAccountChanged);
// To stop listening for a specific event:
// clearConnectListener();
// clearAccountChangedListener();
```
- `removeEventListener(event: PhantomEventType, callback: PhantomEventCallback): void`
- Unregisters a previously registered callback for the specified event.
**Example:**
```typescript
const phantom = createPhantom({ chainPlugins: [createSolanaPlugin()] });
const handleDisconnect = () => {
console.log("Wallet disconnected");
};
phantom.solana.addEventListener("disconnect", handleDisconnect);
// To stop listening for this specific disconnect event:
// phantom.solana.removeEventListener("disconnect", handleDisconnect);
```
### Creating a transaction
Phantom's SDK uses the `@solana/kit` library to create transactions. You can use the `createTransactionMessage` function to create a transaction message.
```typescript
import {
createSolanaRpc,
pipe,
createTransactionMessage,
setTransactionMessageFeePayer,
setTransactionMessageLifetimeUsingBlockhash,
address,
compileTransaction,
} from "@solana/kit";
// Example: Sign and send a transaction
const rpc = createSolanaRpc("https://my-rpc-url.com"); // Replace with your own RPC URL
const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();
const transactionMessage = pipe(
createTransactionMessage({ version: 0 }),
tx => setTransactionMessageFeePayer(address(userPublicKey as string), tx),
tx => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx),
);
const transaction = compileTransaction(transactionMessage);
const { signature } = await phantomInstance.solana.signAndSendTransaction(transaction);
```

Sorry, the diff of this file is not supported yet