@gemini-wallet/core
Complete SDK for integrating with Gemini Wallet, providing wallet connection, transaction signing, and EVM provider functionality.
Overview
@gemini-wallet/core
is a comprehensive wallet SDK that provides everything needed to integrate Gemini Wallet into your application. It includes a complete EVM-compatible provider, wallet connection management, secure storage, and seamless popup-based user interactions.
Features
- 🔗 EVM Provider: Complete Ethereum provider implementation (EIP-1193 compatible)
- 🔒 Secure Communication: PostMessage-based cross-origin communication
- 🪟 Popup Management: Automatic popup window lifecycle management
- 💾 Storage Layer: Persistent storage with localStorage fallback
- 🔄 Event-Driven: Promise-based request/response pattern with event emitters
- ⛓️ Multi-Chain: Support for Ethereum, Polygon, Base, Arbitrum, and testnets
- 🖊️ Sign Operations: Message signing and EIP-712 typed data signing
- 💸 Transaction Support: Send transactions with built-in error handling
- 🌐 Cross-Platform: Works in web browsers and React Native
- ⚡ Lightweight: Minimal dependencies for optimal bundle size
Installation
bun add @gemini-wallet/core
# or
npm install @gemini-wallet/core
# or
yarn add @gemini-wallet/core
# or
pnpm add @gemini-wallet/core
⚠️ YOU MIGHT NOT NEED THIS
For most applications, you should use Wagmi's built-in Gemini connector instead:
import { gemini } from 'wagmi/connectors';
import { createConfig } from 'wagmi';
const config = createConfig({
connectors: [
gemini({
appMetadata: {
name: 'My DApp',
url: 'https://mydapp.com',
}
})
],
// ... rest of wagmi config
});
🎯 Wagmi Integration: Gemini Wallet is available as a default connector in Wagmi v2.x+
📚 Wagmi Docs: wagmi.sh/connectors/gemini
⭐ Status: Currently in PR review - will be available soon!
Usage
This core SDK provides multiple integration levels for advanced use cases:
🚀 Level 1: EVM Provider (Recommended for Custom Implementations)
Use when you need direct provider access or aren't using Wagmi:
import { GeminiWalletProvider } from '@gemini-wallet/core';
const provider = new GeminiWalletProvider({
appMetadata: {
name: 'My DApp',
url: 'https://mydapp.com'
},
chain: { id: 42161 }, // Arbitrum One
});
// Connect and get accounts
const accounts = await provider.request({
method: 'eth_requestAccounts'
});
// Send transaction
const txHash = await provider.request({
method: 'eth_sendTransaction',
params: [{
from: accounts[0],
to: '0x742E4C3B7dcD26e7Ca95C0Ad2F38C61f6F02C4c0',
value: '0x38D7EA4C68000', // 0.001 ETH
}]
});
// Listen for events
provider.on('accountsChanged', (accounts) => {
console.log('Accounts changed:', accounts);
});
⚡ Level 2: Direct Wallet API
Use the wallet class for fine-grained control:
import { GeminiWallet } from '@gemini-wallet/core';
const wallet = new GeminiWallet({
appMetadata: {
name: 'My DApp',
url: 'https://mydapp.com'
},
chain: { id: 42161 }
});
// Connect
const accounts = await wallet.connect();
// Send transaction with error handling
const result = await wallet.sendTransaction({
to: '0x742E4C3B7dcD26e7Ca95C0Ad2F38C61f6F02C4c0',
value: '1000000000000000000', // 1 ETH in wei
});
if (result.error) {
console.error('Transaction failed:', result.error);
} else {
console.log('Transaction hash:', result.hash);
}
⚙️ Level 3: Low-Level Communication
For maximum control over the popup communication:
import { Communicator, GeminiSdkEvent } from '@gemini-wallet/core';
const communicator = new Communicator({
appMetadata: {
name: 'My DApp',
url: 'https://mydapp.com'
}
});
// Send connect request
const response = await communicator.postRequestAndWaitForResponse({
event: GeminiSdkEvent.SDK_CONNECT,
requestId: crypto.randomUUID(),
chainId: 42161,
origin: window.location.origin
});
console.log('Connected address:', response.data.address);
// Listen for specific events
communicator.onMessage(
(message) => message.event === GeminiSdkEvent.SDK_DISCONNECT
).then(() => {
console.log('User disconnected');
});
API Reference
GeminiWalletProvider
EIP-1193 compatible Ethereum provider implementation.
interface GeminiProviderConfig {
appMetadata: AppMetadata;
chain: Chain;
onDisconnectCallback?: () => void;
storage?: IStorage;
}
Methods
request<T>(args: RpcRequestArgs): Promise<T>
- Send RPC requestsdisconnect(): Promise<void>
- Disconnect walletopenSettings(): Promise<void>
- Open wallet settings
Events
accountsChanged
- Emitted when accounts changechainChanged
- Emitted when chain changesconnect
- Emitted on connectiondisconnect
- Emitted on disconnection
GeminiWallet
Direct wallet interface for advanced use cases.
interface GeminiWalletConfig {
appMetadata: AppMetadata;
chain?: Chain;
onDisconnectCallback?: () => void;
storage?: IStorage;
}
Methods
connect(): Promise<Address[]>
- Connect to walletsendTransaction(tx: TransactionRequest): Promise<SendTransactionResponse>
- Send transactionsignData(params: SignMessageParameters): Promise<SignMessageResponse>
- Sign messagesignTypedData(params: SignTypedDataParameters): Promise<SignTypedDataResponse>
- Sign typed dataswitchChain(params: SwitchChainParameters): Promise<string | null>
- Switch chainsopenSettings(): Promise<void>
- Open wallet settings
GeminiStorage
Storage interface for persisting wallet state.
interface IStorage {
setItem(key: string, value: string): Promise<void>;
getItem(key: string): Promise<string | null>;
removeItem(key: string): Promise<void>;
storeObject<T>(key: string, item: T): Promise<void>;
loadObject<T>(key: string, fallback: T): Promise<T>;
}
Communicator
Low-level communication class.
interface CommunicatorConfigParams {
appMetadata: AppMetadata;
onDisconnectCallback?: () => void;
}
Methods
postMessage(message: GeminiSdkMessage): Promise<void>
- Send messagepostRequestAndWaitForResponse<M, R>(request: GeminiSdkMessage): Promise<R>
- Send request and waitonMessage<M, R>(predicate: (message: Partial<M>) => boolean): Promise<R>
- Listen for messageswaitForPopupLoaded(): Promise<Window>
- Wait for popup to load
Message Types
GeminiSdkEvent
Enumeration of all supported events:
POPUP_LOADED
- Popup window has loadedPOPUP_UNLOADED
- Popup window was closedPOPUP_APP_CONTEXT
- App metadata sent to popupSDK_CONNECT
- Connect wallet requestSDK_DISCONNECT
- Disconnect wallet requestSDK_SEND_TRANSACTION
- Send transaction requestSDK_SIGN_MESSAGE
- Sign message requestSDK_SIGN_TYPED_DATA
- Sign typed data requestSDK_SWITCH_CHAIN
- Switch chain requestACCOUNTS_CHANGED
- Accounts changed eventCHAIN_CHANGED
- Chain changed eventDISCONNECT
- Disconnect event
Supported Chains
Gemini Wallet supports the following networks:
Mainnets:
- Ethereum (1)
- Arbitrum One (42161) - Default
- OP Mainnet (10)
- Base (8453)
- Polygon (137)
Testnets:
- Sepolia (11155111)
- Arbitrum Sepolia (421614)
- OP Sepolia (11155420)
- Base Sepolia (84532)
- Polygon Amoy (80002)
Constants
SDK_BACKEND_URL
:"https://keys.gemini.com"
DEFAULT_CHAIN_ID
:42161
(Arbitrum One)SUPPORTED_CHAIN_IDS
: Array of supported chain IDsPOPUP_WIDTH
:420
POPUP_HEIGHT
:650
Security Considerations
- Origin Validation: All messages are validated against the expected origin
- Request ID Matching: Responses are matched to requests using unique IDs
- User Consent: All actions require explicit user approval in the popup
- No Private Keys: The SDK never handles private keys directly
Browser Support
- Chrome/Edge 80+
- Firefox 78+
- Safari 14+
- Opera 67+
Try Gemini Wallet
Experience Gemini Wallet in action:
🔗 keys.gemini.com - Try the wallet interface and see how the SDK integrations work
Integration Examples
The core SDK enables various integration patterns:
- ✅ EIP-1193 compatible provider for any web3 library
- ✅ Custom storage implementations for mobile platforms
- ✅ Event-driven architecture with TypeScript support
- ✅ Multi-chain support with automatic chain switching
- ✅ Error handling with user-friendly error messages
Development
This project uses Bun as the package manager and build tool.
Setup
# Install dependencies
bun install
# Run tests
bun test
# Run tests in watch mode
bun test --watch
# Type checking
bun run typecheck
# Build the package
bun run build
# Development mode (watch mode)
bun run dev
Available Scripts
bun run build
- Build the package for productionbun run dev
- Build in watch mode for developmentbun run test
- Run testsbun run test:watch
- Run tests in watch modebun run typecheck
- Run TypeScript type checkingbun run lint
- Run ESLint (requires configuration)bun run lint:fix
- Fix ESLint issues automatically
Build Output
The build process generates:
dist/index.js
- ESM bundle for Node.jsdist/index.d.ts
- TypeScript declarationsdist/*.d.ts.map
- Source maps for declarations
Contributing
We welcome contributions! Please see our Contributing Guide for details.
License
MIT License - see LICENSE for details.