@story-protocol/global-wallet
v0.0.3
Published
Story Protocol Global Wallet - Cross-app embedded wallet for the IP ecosystem
Downloads
216
Readme
@story-protocol/global-wallet
Cross-app embedded wallet for Story Protocol, enabling seamless wallet portability across applications in the IP ecosystem.
Powered by Dynamic Global Wallets.
Installation
npm install @story-protocol/global-walletQuick Start
1. Import the wallet
Add a single import to your app's entry point. That's it — no API keys, no configuration, no Dynamic account needed.
Important: This package is client-side only. In Next.js App Router, the import must be in a Client Component (a file with
"use client"). It cannot be imported in a Server Component.
// Next.js: app/layout.tsx or Providers.tsx (must be a Client Component)
// Vite/CRA: main.tsx or index.tsx
"use client";
import "@story-protocol/global-wallet/story";2. Use with your wallet library
The Story Global Wallet is auto-discovered via EIP-6963 — no extra configuration needed. It appears alongside MetaMask, Coinbase Wallet, and others in any compatible wallet selector (RainbowKit, ConnectKit, wagmi, Dynamic, etc.).
3. That's it
The user gets the same wallet address across every app that integrates this package.
Gas Sponsorship with ZeroDev
The Story Global Wallet uses ZeroDev for Account Abstraction, enabling gasless (sponsored) transactions. The setup differs depending on whether your app uses RainbowKit/wagmi or the Dynamic SDK directly.
RainbowKit / wagmi
For apps using RainbowKit or wagmi (without Dynamic's SDK), use createKernelClient from the global wallet package to create a ZeroDev kernel client.
Additional dependencies
npm install @zerodev/sdk @dynamic-labs/ethereum-aaCreating a Kernel Client
import GlobalWallet from "@story-protocol/global-wallet";
import { createKernelClient } from "@story-protocol/global-wallet/zerodev";
const smartWallet = GlobalWallet.wallets[0];
const kernelClient = await createKernelClient({
wallet: smartWallet,
chainId: 1514, // Story Mainnet
paymaster: "SPONSOR",
paymasterRpc: "YOUR_ZERODEV_PAYMASTER_RPC",
});Sending Transactions & Resolving UserOp Hashes
Transactions are sent through wagmi's wallet client (which routes through the Global Wallet popup for signing). The kernel client is used only to resolve the UserOperation hash into a real transaction hash via waitForUserOperationReceipt:
import { useWalletClient } from "wagmi";
import { Hash } from "viem";
// Send through wagmi (handles signing via the Global Wallet popup)
const { data: walletClient } = useWalletClient();
const hash = await walletClient.sendTransaction({
account: address,
to: "0x...",
value: BigInt(0),
data: "0x",
});
// Resolve the UserOp hash to a real transaction hash
const receipt = await kernelClient.waitForUserOperationReceipt({
hash: hash as Hash,
});
console.log("Tx hash:", receipt.receipt.transactionHash);Important: Do not use
kernelClient.sendUserOperation()to send transactions — this will hang in the popup. Always send through wagmi's wallet client and use the kernel client only for receipt resolution.
Using with the Story SDK
When using @story-protocol/core-sdk, ZeroDev returns UserOperation hashes instead of regular transaction hashes. The Story SDK needs a txHashResolver to convert these so it can track on-chain events.
Build a txHashResolver from the kernel client and pass it to StoryClient.newClientUseWallet():
import { StoryClient, SupportedChainIds } from "@story-protocol/core-sdk";
import GlobalWallet from "@story-protocol/global-wallet";
import { createKernelClient } from "@story-protocol/global-wallet/zerodev";
import { Hash, http } from "viem";
// Build txHashResolver from kernel client
const kernelClientPromise = createKernelClient({
wallet: GlobalWallet.wallets[0],
chainId: 1514,
paymaster: "SPONSOR",
paymasterRpc: "YOUR_ZERODEV_PAYMASTER_RPC",
});
const txHashResolver = async (userOpHash: Hash): Promise<Hash> => {
const kernelClient = await kernelClientPromise;
const receipt = await kernelClient.waitForUserOperationReceipt({
hash: userOpHash,
});
return receipt.receipt.transactionHash;
};
// Pass the resolver when creating the StoryClient
const storyClient = StoryClient.newClientUseWallet({
transport: http(),
wallet: walletClient, // from wagmi useWalletClient()
chainId: "1514" as SupportedChainIds,
txHashResolver,
});Dynamic SDK
For apps using the Dynamic SDK directly, the ZeroDev kernel client is already available on Dynamic's wallet connector — you don't need to call createKernelClient from the global wallet package.
Prerequisites
- Enable ZeroDev in your Dynamic dashboard with gas sponsorship configured for Story (chain 1514)
- Install the required packages:
npm install @dynamic-labs/ethereum-aa viem- Add
ZeroDevSmartWalletConnectorsto your Dynamic provider:
import { DynamicContextProvider } from "@dynamic-labs/sdk-react-core";
import { EthereumWalletConnectors } from "@dynamic-labs/ethereum";
import { ZeroDevSmartWalletConnectors } from "@dynamic-labs/ethereum-aa";
<DynamicContextProvider
settings={{
environmentId: "YOUR_DYNAMIC_ENVIRONMENT_ID",
walletConnectors: [EthereumWalletConnectors, ZeroDevSmartWalletConnectors],
}}
>
{children}
</DynamicContextProvider>Sending Transactions
With Dynamic, transactions are sent through the wallet client on the connector. Dynamic's ZeroDev integration handles gas sponsorship automatically:
import { useDynamicContext } from "@dynamic-labs/sdk-react-core";
const { primaryWallet } = useDynamicContext();
// Get the viem wallet client from Dynamic's connector
const walletClient = (primaryWallet.connector as any).getWalletClient();
const hash = await walletClient.sendTransaction({
account: primaryWallet.address,
to: "0x...",
value: BigInt(0),
data: "0x",
});Using with the Story SDK
With Dynamic, the ZeroDev kernel client is already available on the connector. Use isZeroDevConnector to detect AA wallets and build a txHashResolver:
import { isZeroDevConnector } from "@dynamic-labs/ethereum-aa";
import { useDynamicContext } from "@dynamic-labs/sdk-react-core";
import { StoryClient, SupportedChainIds } from "@story-protocol/core-sdk";
import { Hash, http } from "viem";
const { primaryWallet } = useDynamicContext();
// Build txHashResolver from the connector's built-in kernel client
let txHashResolver: ((hash: Hash) => Promise<Hash>) | undefined;
if (primaryWallet && isZeroDevConnector(primaryWallet.connector)) {
const connector = primaryWallet.connector as {
kernelClient?: {
waitForUserOperationReceipt: (args: {
hash: Hash;
}) => Promise<{ receipt: { transactionHash: Hash } }>;
};
};
txHashResolver = async (userOpHash: Hash): Promise<Hash> => {
if (!connector.kernelClient) {
throw new Error("ZeroDev kernel client not available");
}
const receipt = await connector.kernelClient.waitForUserOperationReceipt({
hash: userOpHash,
});
return receipt.receipt.transactionHash;
};
}
// Pass the resolver when creating the StoryClient
const walletClient = (primaryWallet.connector as any).getWalletClient();
const storyClient = StoryClient.newClientUseWallet({
transport: http(),
wallet: walletClient,
chainId: "1514" as SupportedChainIds,
txHashResolver,
});How It Works
- You install this package and import it — the EIP-6963 provider is announced automatically
- "Story Global Wallet" appears in your wallet selector UI
- User clicks it — a popup opens to
dynamic.story.foundation - User authenticates or creates a wallet via Dynamic
- Wallet address is returned to your app via encrypted messaging
- All signing requests use the same secure popup flow
Requirements
- Node.js 18+
- A web framework that supports ES modules (Next.js, Vite, CRA, etc.)
Support
- Documentation: https://docs.story.foundation
- Discord: https://discord.gg/storyprotocol
- GitHub Issues: https://github.com/story-protocol/global-wallet/issues
License
MIT
