@rockerone/xprnkit
v0.5.1
Published
> **⚠️ Warning:** Version 0.4.0 introduces breaking changes. Make sure to save your project before migrate.
Downloads
237
Readme
@rockerone/xprnkit
⚠️ Warning: Version 0.4.0 introduces breaking changes. Make sure to save your project before migrate.
React SDK for building dApps on XPR Network. Provides wallet connection, identity proof with pluggable transports, multi-account management, token swap UI, and transaction helpers.
Installation
npm install @rockerone/xprnkitXPRNProvider
Wrap your application with XPRNProvider to enable wallet and blockchain interactions.
import { XPRNProvider } from '@rockerone/xprnkit';
const config = {
chainId: "your-chain-id",
endpoints: ["https://your-endpoint.com"],
dAppName: "Your dApp Name",
requesterAccount: "your-account",
apiMode: "mainnet",
};
function App() {
return (
<XPRNProvider config={config}>
{/* Your app */}
</XPRNProvider>
);
}XPRProviderConfig
| Property | Type | Required | Description |
|---|---|---|---|
| chainId | string | Yes | Blockchain chain ID |
| endpoints | string[] | Yes | RPC endpoints |
| dAppName | string | Yes | Display name for your dApp |
| requesterAccount | string | Yes | Your dApp's account name |
| requesterLogo | string | No | Logo URL for your dApp |
| apiMode | "testnet" \| "mainnet" | Yes | Network environment |
| restoreSession | boolean | No | Restore last active session on page refresh |
| identityProof | XPRNKitIdentityProofConfig | No | Identity proof configuration |
useXPRN
Hook to access the provider context.
import { useXPRN } from '@rockerone/xprnkit';
function MyComponent() {
const {
config,
session,
link,
profile,
identityProof,
identityProofStatus,
identityProofError,
connect,
disconnect,
pushTransaction,
switchToSession,
requestIdentityProof,
} = useXPRN();
}State
| Property | Type | Description |
|---|---|---|
| config | XPRProviderConfig \| null | Current provider configuration |
| session | LinkSession \| null | Active session |
| link | ProtonWebLink \| Link \| null | Proton link object |
| profile | XPRNKitProfile \| null | Profile for the active session |
| identityProof | XPRNKitIdentityProof \| null | Identity proof for the active session |
| identityProofStatus | XPRNKitIdentityProofStatus | Status of the identity proof process |
| identityProofError | Error \| null | Error from the last failed identity proof attempt |
Methods
| Method | Description |
|---|---|
| connect(restore?: boolean) | Connect to wallet. Pass true to restore a previous session silently. |
| disconnect() | Disconnect the current session |
| pushTransaction(actions: any[]) | Push a transaction with the active session |
| switchToSession(actor, permission) | Switch to a different stored session |
| requestIdentityProof() | Trigger the identity proof flow manually. Can retry after error. |
Transaction Example
const { session, pushTransaction } = useXPRN();
await pushTransaction([
{
account: "eosio.token",
name: "transfer",
authorization: [session.auth],
data: {
from: session.auth.actor.toString(),
to: "recipient",
quantity: "1.0000 XPR",
memo: "Hello!",
},
},
]);Identity Proof
Verify wallet ownership by having users sign a message. Supports HTTP, WebSocket, and custom transports.
Flow
- When
requiredistrue, identity proof is triggered automatically on wallet connect. - The provider first checks localStorage for an existing proof and validates it.
- If no valid proof exists, the user is prompted to sign a message.
- The signed message is sent to your backend for verification.
- The backend returns a JWT token, stored in localStorage and exposed via
useXPRN().identityProof. - On failure,
identityProofStatusis set to"error". CallrequestIdentityProof()to retry.
XPRNKitIdentityProofConfig
| Property | Type | Required | Description |
|---|---|---|---|
| required | boolean | Yes | Auto-trigger on connect |
| transport | TransportConfig | No | Transport configuration (defaults to HTTP) |
| createUrl | string | No | HTTP endpoint for signature verification |
| validationUrl | string | No | HTTP endpoint for token validation |
| validationBuffer | number | No | Seconds before expiry to trigger revalidation |
| headers | Record<string, string> | No | Custom headers for requests |
| timeout | number | No | Request timeout in milliseconds |
| createIdentityResponseTransformer | (response: any) => any | No | Transform create response |
| validationIdentityResponseTransformer | (response: any) => any | No | Transform validation response |
HTTP Transport (Default)
const config = {
identityProof: {
required: true,
createUrl: "/api/auth/authorize",
validationUrl: "/api/auth/validate",
},
};WebSocket Transport
const config = {
identityProof: {
required: true,
transport: {
type: 'websocket',
options: {
url: 'wss://api.example.com/identity',
timeout: 15000,
reconnect: true,
reconnectInterval: 5000,
maxReconnectAttempts: 5,
maxReconnectDelay: 30000,
},
},
},
};WebSocket Message Format
// Client -> Server
{ id: string, action: 'createIdentityProof' | 'validateIdentityProof', data: any }
// Server -> Client
{ id: string, action: string, data: any, error?: string }Custom Transport
import { IdentityProofTransport, TransportFactory } from '@rockerone/xprnkit';
class GraphQLTransport implements IdentityProofTransport {
name = 'graphql';
async createIdentityProof(payload, config, signal) {
const response = await fetch('/graphql', {
method: 'POST',
body: JSON.stringify({
query: CREATE_IDENTITY_PROOF_MUTATION,
variables: { input: payload },
}),
signal,
});
return response.json();
}
async validateIdentityProof(proof, config, signal) {
return true;
}
dispose() {}
}
TransportFactory.register('graphql', new GraphQLTransport());
const config = {
identityProof: {
required: true,
transport: { type: 'custom', options: { name: 'graphql' } },
},
};Backend Endpoints
Authorization (createUrl)
// Request body
{
signer: { actor: string, permission: string },
transaction: any,
signatures: string[],
chainId: string,
}
// Response
{
success: true,
validated: true,
actor: string,
permission: string,
token: string,
timestamp: string,
}Validation (validationUrl)
// Request: Authorization header with "Bearer <token>" or body { token: string }
// Response
{ valid: boolean, actor?: string, expiresAt?: string, error?: string }XPRNIdentityProofGate
Conditionally render content based on identity proof status.
import { XPRNIdentityProofGate, useXPRN } from '@rockerone/xprnkit';
function ProtectedContent() {
const { identityProofError, requestIdentityProof } = useXPRN();
return (
<XPRNIdentityProofGate
fallback={<p>Please verify your identity.</p>}
loading={<p>Verifying...</p>}
error={
<div>
<p>Failed: {identityProofError?.message}</p>
<button onClick={requestIdentityProof}>Retry</button>
</div>
}
notConnected={<p>Please connect your wallet first.</p>}
>
<p>Verified content.</p>
</XPRNIdentityProofGate>
);
}| Prop | Type | Description |
|---|---|---|
| children | ReactNode | Content when identity proof is obtained |
| fallback | ReactNode | Content when identity proof is not obtained |
| loading | ReactNode | Content while in progress |
| error | ReactNode | Content on failure (falls back to fallback) |
| notConnected | ReactNode | Content when no session (falls back to fallback) |
useIdentityProofGate
Hook version for programmatic access.
import { useIdentityProofGate } from '@rockerone/xprnkit';
const {
isGateActive,
isConnected,
isInProgress,
hasError,
isVerified,
shouldRenderChildren,
needsIdentityProof,
identityProofStatus,
} = useIdentityProofGate();Multi-Account Support
Connect multiple wallet accounts. The provider maintains one active session at a time. Use switchToSession(actor, permission) to switch between stored accounts.
const { session, switchToSession } = useXPRN();
await switchToSession("otheraccount", "active");Components
XPRNConnectButton
import { XPRNConnectButton } from '@rockerone/xprnkit';
<XPRNConnectButton
onSession={(session, link) => {}}
onProfile={(profile) => {}}
onIdentityProof={(proof) => {}}
>
Connect Wallet
</XPRNConnectButton>Shows "Connect" when disconnected, "Log out (actor)" when connected. Accepts standard button props, variant, size, asChild, and className.
XPRNIdentity
Dropdown showing user identity with avatar and session info.
import { XPRNIdentity } from '@rockerone/xprnkit';
<XPRNIdentity showLogout avatarClassName="custom-avatar">
<div>Dropdown content</div>
</XPRNIdentity>| Prop | Type | Description |
|---|---|---|
| showLogout | boolean | Show logout link |
| activeSessionClassName | string | CSS for active session container |
| dropdownClassName | string | CSS for dropdown |
| avatarClassName | string | CSS for avatar |
| matchDropdownWidth | boolean | Match dropdown width to trigger (default: true) |
| closeOnSelect | boolean | Close dropdown on click (default: true) |
| onSession | (session, link) => void | Session callback |
| onProfile | (profile) => void | Profile callback |
| onIdentityProof | (proof) => void | Identity proof callback |
Sub-components: XPRNAvatar, XPRNSessionActor, XPRNSessionName.
XPRNAccountList
Lists stored accounts with switch, add, and remove functionality.
import { XPRNIdentity, XPRNAccountList } from '@rockerone/xprnkit';
<XPRNIdentity showLogout>
<XPRNAccountList
showAddAccount
showRemove
onSelect={(entry) => {}}
/>
</XPRNIdentity>| Prop | Type | Description |
|---|---|---|
| showRemove | boolean | Show remove button per account |
| showAddAccount | boolean | Show "Add Account" button |
| addAccountLabel | string | Custom add account label |
| renderItem | (props: AccountItemRenderProps) => ReactNode | Custom item renderer |
| onSelect | (entry: XPRNKitProfileStorageEntry) => void | Selection callback |
| onRemove | (entry: XPRNKitProfileStorageEntry) => void | Removal callback |
| onAddAccount | () => void | Add account callback |
XRPNContainer / XPRNLogged / XPRNUnlogged
Conditional rendering based on session state.
import { XRPNContainer, XPRNLogged, XPRNUnlogged } from '@rockerone/xprnkit';
<XRPNContainer noSessionState={<p>Please log in.</p>}>
<p>Welcome!</p>
</XRPNContainer>
<XPRNLogged>
<p>Visible when connected.</p>
</XPRNLogged>
<XPRNUnlogged>
<p>Visible when disconnected.</p>
</XPRNUnlogged>XPRNTransaction
Button that pushes a blockchain transaction.
import { XPRNTransaction } from '@rockerone/xprnkit';
<XPRNTransaction
actions={actions}
onTransactionSuccess={(res) => {}}
onTransactionFail={(err) => {}}
>
Swap
</XPRNTransaction>Shows "Connect" when no session. Accepts actions, onTransactionStart, onTransactionSuccess, onTransactionFail, variant, size, asChild, and className.
Swap Components
XPRNSwap
Complete swap interface out of the box.
import { XPRNSwap } from '@rockerone/xprnkit';
<XPRNSwap />
<XPRNSwap
filters={{ quoteSymbol: "XUSDC" }}
sides={['buy', 'sell']}
/>Custom Swap Layout
import {
XPRNSwapProvider,
XPRNPairsSelector,
XPRNSwapFieldsGroup,
XPRNSwapField,
XPRNSwapSideButton,
XPRNTransaction,
} from '@rockerone/xprnkit';
<XPRNSwapProvider config={{ filters: { quoteSymbol: "SNIPS", baseSymbol: "XUSDC" } }}>
<XPRNPairsSelector />
<XPRNSwapFieldsGroup horizontal>
<XPRNSwapField />
<XPRNSwapSideButton horizontal>Swap</XPRNSwapSideButton>
<XPRNSwapField />
</XPRNSwapFieldsGroup>
<XPRNTransaction>Swap</XPRNTransaction>
</XPRNSwapProvider>useXPRNSwap
| Property/Method | Type | Description |
|---|---|---|
| marketProviders | XPRNMarketProvider[] | Available market providers |
| currentMarketProvider | XPRNMarketProvider \| null | Selected market provider |
| setCurrentMarketProvider | (mp: XPRNMarketProvider) => void | Set market provider |
| refreshMarketPairs | () => void | Refresh pairs |
| currentMarketPairs | XPRNMarketProviderResult[] | Current pairs |
| refreshStatus | ServiceStatus | Refresh status |
| currentSwapPair | XPRNMarketProviderResult \| null | Selected pair |
| setCurrentSwapPair | (pair: XPRNMarketProviderResult) => void | Set pair |
| swapSide | XPRNSwapSide | Current side ("buy" or "sell") |
| setSwapSide | (side: XPRNSwapSide) => void | Set side |
| swapVolume | number | Current volume |
| setSwapVolume | (volume: number) => void | Set volume |
| swapValues | XPRNSwapValues | Current swap values |
| updateSwapValues | (base, quote, lastMutated) => void | Update values |
| swapTransaction | any[] | Current transaction actions |
Sub-components
| Component | Description |
|---|---|
| XPRNSwapProvider | Context provider for swap state. Props: config?: XPRNSwapProviderConfig |
| XPRNSwapFieldsGroup | Groups swap fields, flips on side change. Props: horizontal?: boolean |
| XPRNSwapField | Token amount input. Props: type?: "base" \| "quote" |
| XPRNSwapSideButton | Toggles buy/sell side. Props: horizontal?: boolean |
| XPRNSwapMarketsSelector | Market provider dropdown (renders only if >1 provider) |
| XPRNPairsSelector | Trading pair dropdown. Props: contentClassName, itemsClassName |
Types Reference
type XPRProviderConfig = {
chainId: string;
endpoints: string[];
dAppName: string;
requesterAccount: string;
requesterLogo?: string;
apiMode: "testnet" | "mainnet";
restoreSession?: boolean;
identityProof?: XPRNKitIdentityProofConfig;
};
type XPRNKitIdentityProofConfig = {
required: boolean;
transport?: TransportConfig;
createUrl?: string;
validationUrl?: string;
createIdentityResponseTransformer?: (response: any) => any;
validationIdentityResponseTransformer?: (response: any) => any;
validationBuffer?: number;
headers?: Record<string, string>;
timeout?: number;
};
type TransportConfig = {
type: 'http' | 'websocket' | 'custom';
transport?: IdentityProofTransport;
options?: Record<string, any>;
};
type WebSocketOptions = {
url: string;
timeout?: number;
reconnect?: boolean;
reconnectInterval?: number;
maxReconnectAttempts?: number;
maxReconnectDelay?: number;
};
type XPRNKitIdentityProof = {
auth: { actor: string; permission: string };
token: string;
metadata: { [key: string]: any };
};
type XPRNKitIdentityProofPayload = {
signer: { actor: string; permission: string };
transaction: any;
signatures: string[];
chainId: string;
};
type XPRNKitIdentityProofStatus =
| "idle" | "signing" | "verifying"
| "validating" | "success" | "expired" | "error";
type XPRNKitProfile = {
displayName: string;
avatar?: string;
isKyc: boolean;
};
type XPRNKitProfileStorageEntry = {
auth: { actor: string; permission: string };
chainId: string;
profile: XPRNKitProfile;
};
type ServiceStatus = 'idle' | 'pending' | 'success' | 'fail';Utilities
toPrecision
Format a number to a given precision.
function toPrecision(
value: number,
precision: number,
mode?: "ceil" | "floor" | "round" | "none",
forceDecimal?: boolean
): string;toPrecision(3.14159, 2) // "3.15"
toPrecision(3.14159, 2, "floor") // "3.14"
toPrecision(3, 2, "none", true) // "3.00"
toPrecision(3, 2, "ceil", false) // "3"