orbital-wallet-provider 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,223 @@
1
+ <div align="center">
2
+ <img src="logo.svg" width="80" height="80" alt="Orbital Wallet" />
3
+ <h1>Orbital Wallet Provider</h1>
4
+ <p>React provider and hooks for integrating the <a href="https://github.com/blademaster-888/radiant-orbital-wallet">Orbital Wallet</a> Chrome extension into any React or Next.js application.</p>
5
+ <p>Orbital Wallet is a non-custodial browser wallet for the <a href="https://radiantblockchain.org">Radiant</a> L1 blockchain with Glyph NFT support.</p>
6
+ </div>
7
+
8
+ ---
9
+
10
+ ## Installation
11
+
12
+ ```bash
13
+ npm install orbital-wallet-provider
14
+ # or
15
+ pnpm add orbital-wallet-provider
16
+ ```
17
+
18
+ React 17+ is required as a peer dependency.
19
+
20
+ ---
21
+
22
+ ## Quick start
23
+
24
+ ### 1. Wrap your app with `OrbitalProvider`
25
+
26
+ ```tsx
27
+ import { OrbitalProvider } from "orbital-wallet-provider"
28
+
29
+ export default function App({ children }) {
30
+ return (
31
+ <OrbitalProvider>
32
+ {children}
33
+ </OrbitalProvider>
34
+ )
35
+ }
36
+ ```
37
+
38
+ The provider automatically detects the injected `window.orbital` object and **restores connection state on page refresh** if the site was previously authorised — no manual reconnect needed.
39
+
40
+ ### 2. Use the `useOrbitalWallet` hook
41
+
42
+ ```tsx
43
+ import { useOrbitalWallet } from "orbital-wallet-provider"
44
+
45
+ export function ConnectButton() {
46
+ const { isReady, isConnected, connect, disconnect, addresses, balance } = useOrbitalWallet()
47
+
48
+ if (!isReady) {
49
+ return (
50
+ <a href="https://github.com/blademaster-888/radiant-orbital-wallet" target="_blank">
51
+ Install Orbital Wallet
52
+ </a>
53
+ )
54
+ }
55
+
56
+ if (!isConnected) {
57
+ return <button onClick={() => connect()}>Connect Wallet</button>
58
+ }
59
+
60
+ return (
61
+ <div>
62
+ <p>{addresses?.rxdAddress}</p>
63
+ <p>{balance ? (balance.total / 1e8).toFixed(8) : "—"} RXD</p>
64
+ <button onClick={() => disconnect()}>Disconnect</button>
65
+ </div>
66
+ )
67
+ }
68
+ ```
69
+
70
+ ---
71
+
72
+ ## API
73
+
74
+ ### `<OrbitalProvider>`
75
+
76
+ Wrap your application (or any subtree that needs wallet access) with this provider. It:
77
+
78
+ - Polls for `window.orbital` for up to 5 seconds after mount (the extension may inject after the page loads)
79
+ - Calls `isConnected()` once the extension is detected — if the site was previously authorised, `isConnected` and `addresses` are restored silently without opening a popup
80
+ - Listens for `"signedOut"` events from the extension and clears connection state accordingly
81
+
82
+ ### `useOrbitalWallet()`
83
+
84
+ Returns the full wallet context:
85
+
86
+ | Property | Type | Description |
87
+ |---|---|---|
88
+ | `isReady` | `boolean` | Extension detected in the page |
89
+ | `isConnected` | `boolean` | Site is connected and authorised |
90
+ | `pubKey` | `string \| null` | Identity public key (hex) |
91
+ | `addresses` | `OrbitalAddresses \| null` | RXD, Glyph, and identity addresses |
92
+ | `balance` | `RxdBalance \| null` | Confirmed + unconfirmed RXD (photons) |
93
+ | `connect()` | `() => Promise<PubKey \| undefined>` | Request connection — opens wallet popup |
94
+ | `disconnect()` | `() => Promise<void>` | Disconnect and clear state |
95
+ | `getAddresses()` | `() => Promise<OrbitalAddresses \| undefined>` | Refresh addresses from extension |
96
+ | `getBalance()` | `() => Promise<RxdBalance \| undefined>` | Refresh RXD balance |
97
+ | `signMessage(params)` | `(SignMessageParams) => Promise<SignMessageResponse \| undefined>` | Sign a message with the RXD key |
98
+ | `sendRxd(params)` | `(SendRxdParams[]) => Promise<SendRxdResponse[] \| undefined>` | Send RXD to one or more addresses |
99
+ | `transferGlyphFt(params)` | `(GlyphFtParams) => Promise<GlyphFtResponse \| undefined>` | Transfer a Glyph fungible token |
100
+ | `transferGlyphNft(params)` | `(GlyphNftParams) => Promise<GlyphNftResponse \| undefined>` | Transfer a Glyph NFT |
101
+ | `purchaseGlyphNft(params)` | `(PurchaseGlyphNftParams) => Promise<PurchaseGlyphNftResponse \| undefined>` | Purchase a listed Glyph NFT |
102
+ | `getSignatures(params)` | `(GetSignaturesParams) => Promise<SignatureResponse[] \| undefined>` | Sign specific inputs in a raw tx |
103
+ | `broadcast(params)` | `(BroadcastParams) => Promise<BroadcastResponse \| undefined>` | Broadcast a fully signed raw transaction |
104
+ | `encrypt(params)` | `(EncryptParams) => Promise<string \| undefined>` | ECIES-encrypt a message |
105
+ | `decrypt(params)` | `(DecryptParams) => Promise<string \| undefined>` | ECIES-decrypt a ciphertext |
106
+ | `getExchangeRate()` | `() => Promise<ExchangeRateResponse \| undefined>` | RXD/fiat exchange rate |
107
+ | `getSocialProfile()` | `() => Promise<OrbitalSocialProfile \| undefined>` | Social profile for the connected account |
108
+ | `on(event, handler)` | | Subscribe to a wallet event |
109
+ | `off(event, handler)` | | Unsubscribe from a wallet event |
110
+
111
+ ### Key types
112
+
113
+ ```ts
114
+ interface OrbitalAddresses {
115
+ rxdAddress: string // P2PKH address — used for RXD payments and signing
116
+ glyphAddress: string // Address for Glyph NFT/FT operations
117
+ identityAddress: string // Identity derivation address
118
+ }
119
+
120
+ interface RxdBalance {
121
+ confirmed: number // photons (1 RXD = 100_000_000 photons)
122
+ unconfirmed: number
123
+ total: number
124
+ }
125
+
126
+ interface SignMessageResponse {
127
+ sig: string // base64 Bitcoin-style signed message
128
+ address: string // RXD address that produced the signature
129
+ pubKey: string // hex-encoded compressed public key
130
+ }
131
+ ```
132
+
133
+ ### Events
134
+
135
+ | Event | Fired when |
136
+ |---|---|
137
+ | `"signedOut"` | User locks the wallet or signs out |
138
+ | `"connect"` | Site connection approved |
139
+ | `"disconnect"` | Site disconnected from wallet |
140
+
141
+ ### `OrbitalIcon`
142
+
143
+ Inline SVG of the Orbital Wallet logo — useful for connect buttons and nav bars.
144
+
145
+ ```tsx
146
+ import { OrbitalIcon } from "orbital-wallet-provider"
147
+
148
+ // defaults: size="24px", color="currentColor"
149
+ <OrbitalIcon size="32px" color="#00d4ff" />
150
+ ```
151
+
152
+ ### Utility helpers
153
+
154
+ ```ts
155
+ import {
156
+ photonsToRxd,
157
+ rxdToPhotons,
158
+ formatRxd,
159
+ formatToken,
160
+ truncateAddress,
161
+ glyphExplorerUrl,
162
+ explorerTxUrl,
163
+ isValidRxdAddress,
164
+ } from "orbital-wallet-provider"
165
+
166
+ photonsToRxd(100_000_000) // → 1
167
+ rxdToPhotons(1) // → 100000000
168
+ formatRxd(100_000_000) // → "1.00000000 RXD"
169
+ formatToken(1000000, "BNET", 0) // → "1,000,000 BNET"
170
+ truncateAddress("1AbCdEfGhIjKlMn") // → "1AbCdE…lMn"
171
+ isValidRxdAddress("1AbC...") // → true / false
172
+ ```
173
+
174
+ ---
175
+
176
+ ## Sign-in pattern (wallet authentication)
177
+
178
+ Prove wallet ownership to a backend using a Bitcoin-style signed message. `signMessage` always uses the **RXD key** (`addresses.rxdAddress`) so the address you register with the backend matches the address shown to the user in the wallet.
179
+
180
+ ```tsx
181
+ import { useOrbitalWallet } from "orbital-wallet-provider"
182
+
183
+ const { signMessage, addresses } = useOrbitalWallet()
184
+
185
+ async function signIn() {
186
+ // 1. Request a nonce from your backend for the user's RXD address
187
+ const { nonce, message } = await fetch("/api/auth/nonce", {
188
+ method: "POST",
189
+ headers: { "Content-Type": "application/json" },
190
+ body: JSON.stringify({ address: addresses?.rxdAddress }),
191
+ }).then(r => r.json())
192
+
193
+ // 2. Ask the wallet to sign — opens the Orbital Wallet popup
194
+ // Returns undefined if the user cancels
195
+ const signed = await signMessage({ message })
196
+ if (!signed) throw new Error("Sign-in cancelled")
197
+
198
+ // 3. Verify on your backend
199
+ // signed.address is the RXD address that produced the signature
200
+ // signed.sig is a base64 Bitcoin Signed Message (65 bytes)
201
+ // signed.pubKey is the hex-encoded compressed public key
202
+ const { token } = await fetch("/api/auth/verify", {
203
+ method: "POST",
204
+ headers: { "Content-Type": "application/json" },
205
+ body: JSON.stringify({
206
+ address: signed.address,
207
+ message,
208
+ nonce,
209
+ signature: signed.sig,
210
+ }),
211
+ }).then(r => r.json())
212
+
213
+ return token
214
+ }
215
+ ```
216
+
217
+ > **Backend verification**: the signature is a standard Bitcoin Signed Message. Use `coincurve`, `bitcoinjs-message`, or any BSM-compatible library on the server. The message is hashed as `SHA256d("Bitcoin Signed Message:\n" + varint(len) + message)` and the signature is 65 bytes: `[recovery_flag][r][s]`.
218
+
219
+ ---
220
+
221
+ ## License
222
+
223
+ MIT
@@ -0,0 +1,264 @@
1
+ import React from 'react';
2
+
3
+ /** Satoshi-equivalent unit on Radiant. 1 RXD = 100_000_000 photons */
4
+ type Photons = number;
5
+ /** A Radiant P2PKH address string */
6
+ type RxdAddress = string;
7
+ /** Hex-encoded public key */
8
+ type PubKey = string;
9
+ /** Hex-encoded raw transaction */
10
+ type RawTx = string;
11
+ /** Hex-encoded transaction ID */
12
+ type TxID = string;
13
+ /** Glyph token contract reference (20-byte hex string) */
14
+ type GlyphRef = string;
15
+ interface OrbitalAddresses {
16
+ rxdAddress: RxdAddress;
17
+ glyphAddress: RxdAddress;
18
+ identityAddress: RxdAddress;
19
+ }
20
+ interface RxdBalance {
21
+ confirmed: Photons;
22
+ unconfirmed: Photons;
23
+ total: Photons;
24
+ }
25
+ interface SendRxdParams {
26
+ address: RxdAddress;
27
+ satoshis: Photons;
28
+ }
29
+ interface SendRxdResponse {
30
+ txid: TxID;
31
+ rawtx: RawTx;
32
+ }
33
+ interface GlyphFtParams {
34
+ tokenRef: GlyphRef;
35
+ address: RxdAddress;
36
+ amount: number;
37
+ }
38
+ interface GlyphFtResponse {
39
+ txid: TxID;
40
+ rawtx: RawTx;
41
+ }
42
+ interface GlyphNftParams {
43
+ tokenRef: GlyphRef;
44
+ address: RxdAddress;
45
+ }
46
+ interface GlyphNftResponse {
47
+ txid: TxID;
48
+ rawtx: RawTx;
49
+ }
50
+ interface PurchaseGlyphNftParams {
51
+ listingTxid: TxID;
52
+ priceSatoshis: Photons;
53
+ sellerAddress: RxdAddress;
54
+ }
55
+ interface PurchaseGlyphNftResponse {
56
+ txid: TxID;
57
+ rawtx: RawTx;
58
+ }
59
+ interface SignMessageParams {
60
+ message: string;
61
+ address?: RxdAddress;
62
+ }
63
+ interface SignMessageResponse {
64
+ sig: string;
65
+ address: RxdAddress;
66
+ pubKey: PubKey;
67
+ }
68
+ interface SignatureRequest {
69
+ prevTxid: TxID;
70
+ outputIndex: number;
71
+ inputIndex: number;
72
+ satoshis: Photons;
73
+ address: RxdAddress;
74
+ script: string;
75
+ sigHashType: number;
76
+ csIdx?: number;
77
+ data?: Record<string, unknown>;
78
+ }
79
+ interface SignatureResponse {
80
+ sig: string;
81
+ pubKey: PubKey;
82
+ sigHashType: number;
83
+ csIdx?: number;
84
+ data?: Record<string, unknown>;
85
+ }
86
+ interface GetSignaturesParams {
87
+ rawtx: RawTx;
88
+ sigRequests: SignatureRequest[];
89
+ }
90
+ interface BroadcastParams {
91
+ rawtx: RawTx;
92
+ }
93
+ interface BroadcastResponse {
94
+ txid: TxID;
95
+ }
96
+ interface EncryptParams {
97
+ message: string;
98
+ pubKey: PubKey;
99
+ }
100
+ interface DecryptParams {
101
+ ciphertext: string;
102
+ }
103
+ interface ExchangeRateResponse {
104
+ currency: string;
105
+ rate: number;
106
+ }
107
+ interface OrbitalSocialProfile {
108
+ displayName?: string;
109
+ avatar?: string;
110
+ idAddress?: RxdAddress;
111
+ }
112
+ type OrbitalWalletEventName = "signedOut" | "connect" | "disconnect";
113
+ type OrbitalWalletEventHandler = () => void;
114
+ interface OrbitalWalletProvider {
115
+ isReady: boolean;
116
+ isConnected: () => Promise<boolean>;
117
+ connect: () => Promise<PubKey | undefined>;
118
+ disconnect: () => Promise<void>;
119
+ getAddresses: () => Promise<OrbitalAddresses | undefined>;
120
+ getBalance: () => Promise<RxdBalance | undefined>;
121
+ sendRxd: (params: SendRxdParams[]) => Promise<SendRxdResponse[] | undefined>;
122
+ transferGlyphFt: (params: GlyphFtParams) => Promise<GlyphFtResponse | undefined>;
123
+ transferGlyphNft: (params: GlyphNftParams) => Promise<GlyphNftResponse | undefined>;
124
+ purchaseGlyphNft: (params: PurchaseGlyphNftParams) => Promise<PurchaseGlyphNftResponse | undefined>;
125
+ signMessage: (params: SignMessageParams) => Promise<SignMessageResponse | undefined>;
126
+ getSignatures: (params: GetSignaturesParams) => Promise<SignatureResponse[] | undefined>;
127
+ broadcast: (params: BroadcastParams) => Promise<BroadcastResponse | undefined>;
128
+ encrypt: (params: EncryptParams) => Promise<string | undefined>;
129
+ decrypt: (params: DecryptParams) => Promise<string | undefined>;
130
+ getExchangeRate: () => Promise<ExchangeRateResponse | undefined>;
131
+ getSocialProfile: () => Promise<OrbitalSocialProfile | undefined>;
132
+ on: (event: OrbitalWalletEventName, handler: OrbitalWalletEventHandler) => void;
133
+ off: (event: OrbitalWalletEventName, handler: OrbitalWalletEventHandler) => void;
134
+ }
135
+ declare global {
136
+ interface Window {
137
+ orbital?: OrbitalWalletProvider;
138
+ }
139
+ }
140
+
141
+ interface OrbitalWalletContextState {
142
+ /**
143
+ * True when the Orbital Wallet extension is installed and unlocked.
144
+ * Check this before calling connect().
145
+ */
146
+ isReady: boolean;
147
+ /**
148
+ * True when the user has actively connected this site in the wallet.
149
+ */
150
+ isConnected: boolean;
151
+ /**
152
+ * The identity public key returned by connect(), null when not connected.
153
+ */
154
+ pubKey: PubKey | null;
155
+ /**
156
+ * Addresses for the connected account, null when not connected.
157
+ */
158
+ addresses: OrbitalAddresses | null;
159
+ /**
160
+ * Confirmed + unconfirmed RXD balance, null when not connected.
161
+ */
162
+ balance: RxdBalance | null;
163
+ /** Request connection. Opens the Orbital Wallet popup. */
164
+ connect: () => Promise<PubKey | undefined>;
165
+ /** Disconnect this site from the wallet. */
166
+ disconnect: () => Promise<void>;
167
+ /** Refresh addresses from the wallet. */
168
+ getAddresses: () => Promise<OrbitalAddresses | undefined>;
169
+ /** Refresh balance from the wallet. */
170
+ getBalance: () => Promise<RxdBalance | undefined>;
171
+ /** Send RXD to one or more recipients. */
172
+ sendRxd: (params: SendRxdParams[]) => Promise<SendRxdResponse[] | undefined>;
173
+ /** Transfer a Glyph fungible token (FT/BNET/etc). */
174
+ transferGlyphFt: (params: GlyphFtParams) => Promise<GlyphFtResponse | undefined>;
175
+ /** Transfer a Glyph NFT. */
176
+ transferGlyphNft: (params: GlyphNftParams) => Promise<GlyphNftResponse | undefined>;
177
+ /** Purchase a listed Glyph NFT from a P2P listing. */
178
+ purchaseGlyphNft: (params: PurchaseGlyphNftParams) => Promise<PurchaseGlyphNftResponse | undefined>;
179
+ /** Sign a human-readable message. */
180
+ signMessage: (params: SignMessageParams) => Promise<SignMessageResponse | undefined>;
181
+ /** Sign specific inputs in a raw tx (PSBT-style). */
182
+ getSignatures: (params: GetSignaturesParams) => Promise<SignatureResponse[] | undefined>;
183
+ /** Broadcast a fully signed raw transaction. */
184
+ broadcast: (params: BroadcastParams) => Promise<BroadcastResponse | undefined>;
185
+ /** ECIES-encrypt a message to a public key. */
186
+ encrypt: (params: EncryptParams) => Promise<string | undefined>;
187
+ /** ECIES-decrypt a ciphertext with the wallet private key. */
188
+ decrypt: (params: DecryptParams) => Promise<string | undefined>;
189
+ /** Get the current RXD/fiat exchange rate. */
190
+ getExchangeRate: () => Promise<ExchangeRateResponse | undefined>;
191
+ /** Get social/profile info for the connected account. */
192
+ getSocialProfile: () => Promise<OrbitalSocialProfile | undefined>;
193
+ /** Register an event listener on the wallet. */
194
+ on: (event: OrbitalWalletEventName, handler: OrbitalWalletEventHandler) => void;
195
+ /** Remove an event listener from the wallet. */
196
+ off: (event: OrbitalWalletEventName, handler: OrbitalWalletEventHandler) => void;
197
+ }
198
+ interface OrbitalProviderProps {
199
+ children: React.ReactNode;
200
+ }
201
+ declare const OrbitalProvider: React.FC<OrbitalProviderProps>;
202
+ /**
203
+ * useOrbitalWallet — access the connected Orbital Wallet state and methods.
204
+ *
205
+ * Must be used inside <OrbitalProvider>.
206
+ *
207
+ * @example
208
+ * const { isReady, connect, balance, transferGlyphFt } = useOrbitalWallet();
209
+ */
210
+ declare const useOrbitalWallet: () => OrbitalWalletContextState;
211
+
212
+ interface OrbitalIconProps {
213
+ size?: string | number;
214
+ color?: string;
215
+ className?: string;
216
+ style?: React.CSSProperties;
217
+ }
218
+ /**
219
+ * OrbitalIcon — the Orbital Wallet logo as an inline SVG.
220
+ * Drop-in replacement for YoursIcon from yours-wallet-provider.
221
+ *
222
+ * @example
223
+ * <OrbitalIcon size="32px" color="#00d4ff" />
224
+ */
225
+ declare const OrbitalIcon: React.FC<OrbitalIconProps>;
226
+
227
+ /**
228
+ * orbital-wallet-provider — utils.ts
229
+ * Helper functions for working with Radiant/Glyph values.
230
+ */
231
+ /** Convert photons (base unit) to RXD */
232
+ declare const photonsToRxd: (photons: number) => number;
233
+ /** Convert RXD to photons */
234
+ declare const rxdToPhotons: (rxd: number) => number;
235
+ /**
236
+ * Format a photon amount as a human-readable RXD string.
237
+ * e.g. formatRxd(100000000) → "1.00000000 RXD"
238
+ */
239
+ declare const formatRxd: (photons: number, decimals?: number) => string;
240
+ /**
241
+ * Format a Glyph token amount using its decimals.
242
+ * e.g. formatToken(1000000, "BNET", 0) → "1000000 BNET"
243
+ */
244
+ declare const formatToken: (amount: number, ticker: string, decimals?: number) => string;
245
+ /**
246
+ * Truncate a Radiant address for display.
247
+ * e.g. "1AbCd...xYz9"
248
+ */
249
+ declare const truncateAddress: (address: string, prefixLen?: number, suffixLen?: number) => string;
250
+ /**
251
+ * Returns the Glyph explorer URL for a given token ref.
252
+ */
253
+ declare const glyphExplorerUrl: (ref: string) => string;
254
+ /**
255
+ * Returns the block explorer TX URL.
256
+ */
257
+ declare const explorerTxUrl: (txid: string) => string;
258
+ /**
259
+ * Validate a Radiant P2PKH address (basic check).
260
+ * Radiant addresses start with '1' (mainnet).
261
+ */
262
+ declare const isValidRxdAddress: (address: string) => boolean;
263
+
264
+ export { type BroadcastParams, type BroadcastResponse, type DecryptParams, type EncryptParams, type ExchangeRateResponse, type GetSignaturesParams, type GlyphFtParams, type GlyphFtResponse, type GlyphNftParams, type GlyphNftResponse, type GlyphRef, type OrbitalAddresses, OrbitalIcon, type OrbitalIconProps, OrbitalProvider, type OrbitalProviderProps, type OrbitalSocialProfile, type OrbitalWalletContextState, type OrbitalWalletEventHandler, type OrbitalWalletEventName, type OrbitalWalletProvider, type Photons, type PubKey, type PurchaseGlyphNftParams, type PurchaseGlyphNftResponse, type RawTx, type RxdAddress, type RxdBalance, type SendRxdParams, type SendRxdResponse, type SignMessageParams, type SignMessageResponse, type SignatureRequest, type SignatureResponse, type TxID, explorerTxUrl, formatRxd, formatToken, glyphExplorerUrl, isValidRxdAddress, photonsToRxd, rxdToPhotons, truncateAddress, useOrbitalWallet };