@futurekode/stablepay-react 0.1.0 → 0.2.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/dist/index.d.mts CHANGED
@@ -13,11 +13,15 @@ type StablePayPayArgs = {
13
13
  type StablePayProviderProps = {
14
14
  children: React.ReactNode;
15
15
  endpoint?: string;
16
+ to?: string;
16
17
  };
17
18
  type StablePayProps = {
18
19
  amount: number;
19
- to: string;
20
- children: React.ReactElement;
20
+ to?: string;
21
+ children: React.ReactElement<{
22
+ onClick?: (event: React.MouseEvent) => void;
23
+ disabled?: boolean;
24
+ }>;
21
25
  onSuccess?: (payload: StablePaySuccessPayload) => void;
22
26
  onError?: (error: Error) => void;
23
27
  };
@@ -28,16 +32,24 @@ type UseStablePayResult = {
28
32
  signature: string | null;
29
33
  };
30
34
 
31
- declare function StablePayProvider({ children, endpoint, }: StablePayProviderProps): react_jsx_runtime.JSX.Element;
35
+ declare function StablePayProvider({ children, endpoint, to, }: StablePayProviderProps): react_jsx_runtime.JSX.Element;
32
36
 
33
- type ClickableChildProps = {
37
+ declare function StablePay({ amount, to, children, onSuccess, onError, }: StablePayProps): React.ReactElement<{
34
38
  onClick?: (event: React.MouseEvent) => void;
35
39
  disabled?: boolean;
36
- "aria-busy"?: boolean;
37
- "data-stablepay-loading"?: string;
38
- };
39
- declare function StablePay({ amount, to, children, onSuccess, onError, }: StablePayProps): React.ReactElement<ClickableChildProps, string | React.JSXElementConstructor<any>>;
40
+ }, string | React.JSXElementConstructor<any>>;
40
41
 
41
- declare function useStablePay(): UseStablePayResult;
42
+ type PayArgs = {
43
+ amount: number;
44
+ to: string;
45
+ onSuccess?: (signature: string) => void;
46
+ onError?: (error: Error) => void;
47
+ };
48
+ declare function useStablePay(): {
49
+ pay: ({ amount, to, onSuccess, onError }: PayArgs) => Promise<void>;
50
+ loading: boolean;
51
+ error: Error | null;
52
+ signature: string | null;
53
+ };
42
54
 
43
55
  export { StablePay, type StablePayPayArgs, type StablePayProps, StablePayProvider, type StablePayProviderProps, type StablePaySuccessPayload, type UseStablePayResult, useStablePay };
package/dist/index.d.ts CHANGED
@@ -13,11 +13,15 @@ type StablePayPayArgs = {
13
13
  type StablePayProviderProps = {
14
14
  children: React.ReactNode;
15
15
  endpoint?: string;
16
+ to?: string;
16
17
  };
17
18
  type StablePayProps = {
18
19
  amount: number;
19
- to: string;
20
- children: React.ReactElement;
20
+ to?: string;
21
+ children: React.ReactElement<{
22
+ onClick?: (event: React.MouseEvent) => void;
23
+ disabled?: boolean;
24
+ }>;
21
25
  onSuccess?: (payload: StablePaySuccessPayload) => void;
22
26
  onError?: (error: Error) => void;
23
27
  };
@@ -28,16 +32,24 @@ type UseStablePayResult = {
28
32
  signature: string | null;
29
33
  };
30
34
 
31
- declare function StablePayProvider({ children, endpoint, }: StablePayProviderProps): react_jsx_runtime.JSX.Element;
35
+ declare function StablePayProvider({ children, endpoint, to, }: StablePayProviderProps): react_jsx_runtime.JSX.Element;
32
36
 
33
- type ClickableChildProps = {
37
+ declare function StablePay({ amount, to, children, onSuccess, onError, }: StablePayProps): React.ReactElement<{
34
38
  onClick?: (event: React.MouseEvent) => void;
35
39
  disabled?: boolean;
36
- "aria-busy"?: boolean;
37
- "data-stablepay-loading"?: string;
38
- };
39
- declare function StablePay({ amount, to, children, onSuccess, onError, }: StablePayProps): React.ReactElement<ClickableChildProps, string | React.JSXElementConstructor<any>>;
40
+ }, string | React.JSXElementConstructor<any>>;
40
41
 
41
- declare function useStablePay(): UseStablePayResult;
42
+ type PayArgs = {
43
+ amount: number;
44
+ to: string;
45
+ onSuccess?: (signature: string) => void;
46
+ onError?: (error: Error) => void;
47
+ };
48
+ declare function useStablePay(): {
49
+ pay: ({ amount, to, onSuccess, onError }: PayArgs) => Promise<void>;
50
+ loading: boolean;
51
+ error: Error | null;
52
+ signature: string | null;
53
+ };
42
54
 
43
55
  export { StablePay, type StablePayPayArgs, type StablePayProps, StablePayProvider, type StablePayProviderProps, type StablePaySuccessPayload, type UseStablePayResult, useStablePay };
package/dist/index.js CHANGED
@@ -37,28 +37,41 @@ __export(index_exports, {
37
37
  module.exports = __toCommonJS(index_exports);
38
38
 
39
39
  // src/provider/StablePayProvider.tsx
40
- var import_react = require("react");
40
+ var import_react2 = require("react");
41
41
  var import_wallet_adapter_react = require("@solana/wallet-adapter-react");
42
42
  var import_wallet_adapter_react_ui = require("@solana/wallet-adapter-react-ui");
43
43
  var import_wallet_adapter_wallets = require("@solana/wallet-adapter-wallets");
44
+
45
+ // src/context/StablePayContext.ts
46
+ var import_react = require("react");
47
+ var StablePayContext = (0, import_react.createContext)({});
48
+ function useStablePayContext() {
49
+ return (0, import_react.useContext)(StablePayContext);
50
+ }
51
+ var StablePayContext_default = StablePayContext;
52
+
53
+ // src/provider/StablePayProvider.tsx
44
54
  var import_styles = require("@solana/wallet-adapter-react-ui/styles.css");
45
55
  var import_jsx_runtime = require("react/jsx-runtime");
56
+ var DEFAULT_RPC = "https://mainnet.helius-rpc.com/?api-key=a90079ee-e2b5-438e-9b1a-2ae7ba8c1417";
46
57
  function StablePayProvider({
47
58
  children,
48
- endpoint = "https://api.mainnet-beta.solana.com"
59
+ endpoint,
60
+ to
49
61
  }) {
50
- const wallets = (0, import_react.useMemo)(
62
+ const resolvedEndpoint = endpoint ?? DEFAULT_RPC;
63
+ const wallets = (0, import_react2.useMemo)(
51
64
  () => [new import_wallet_adapter_wallets.PhantomWalletAdapter(), new import_wallet_adapter_wallets.SolflareWalletAdapter()],
52
65
  []
53
66
  );
54
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_wallet_adapter_react.ConnectionProvider, { endpoint, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_wallet_adapter_react.WalletProvider, { wallets, autoConnect: true, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_wallet_adapter_react_ui.WalletModalProvider, { children }) }) });
67
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StablePayContext_default.Provider, { value: { defaultTo: to }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_wallet_adapter_react.ConnectionProvider, { endpoint: resolvedEndpoint, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_wallet_adapter_react.WalletProvider, { wallets, autoConnect: true, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_wallet_adapter_react_ui.WalletModalProvider, { children }) }) }) });
55
68
  }
56
69
 
57
70
  // src/components/StablePay.tsx
58
- var import_react3 = __toESM(require("react"));
71
+ var import_react4 = __toESM(require("react"));
59
72
 
60
73
  // src/hooks/useStablePay.ts
61
- var import_react2 = require("react");
74
+ var import_react3 = require("react");
62
75
  var import_web32 = require("@solana/web3.js");
63
76
  var import_wallet_adapter_react2 = require("@solana/wallet-adapter-react");
64
77
  var import_wallet_adapter_react_ui2 = require("@solana/wallet-adapter-react-ui");
@@ -118,49 +131,104 @@ async function buildUsdcTransfer({
118
131
  // src/hooks/useStablePay.ts
119
132
  function useStablePay() {
120
133
  const { connection } = (0, import_wallet_adapter_react2.useConnection)();
121
- const { connected, publicKey, sendTransaction, connect } = (0, import_wallet_adapter_react2.useWallet)();
134
+ const { connected, publicKey, sendTransaction } = (0, import_wallet_adapter_react2.useWallet)();
122
135
  const { setVisible } = (0, import_wallet_adapter_react_ui2.useWalletModal)();
123
- const [loading, setLoading] = (0, import_react2.useState)(false);
124
- const [signature, setSignature] = (0, import_react2.useState)(null);
125
- const [error, setError] = (0, import_react2.useState)(null);
126
- const pay = (0, import_react2.useCallback)(
136
+ const [loading, setLoading] = (0, import_react3.useState)(false);
137
+ const [signature, setSignature] = (0, import_react3.useState)(null);
138
+ const [error, setError] = (0, import_react3.useState)(null);
139
+ const [pendingPayment, setPendingPayment] = (0, import_react3.useState)(
140
+ null
141
+ );
142
+ const executePayment = (0, import_react3.useCallback)(
127
143
  async ({ amount, to }) => {
144
+ if (!publicKey) {
145
+ throw new Error("Wallet not connected");
146
+ }
147
+ let recipient;
148
+ try {
149
+ recipient = new import_web32.PublicKey(to);
150
+ } catch {
151
+ throw new Error("Invalid recipient wallet address");
152
+ }
153
+ const transaction = await buildUsdcTransfer({
154
+ connection,
155
+ fromWallet: publicKey,
156
+ toWallet: recipient,
157
+ amount
158
+ });
159
+ const sig = await sendTransaction(transaction, connection);
160
+ await connection.confirmTransaction(sig, "confirmed");
161
+ setSignature(sig);
162
+ return sig;
163
+ },
164
+ [publicKey, connection, sendTransaction]
165
+ );
166
+ const pay = (0, import_react3.useCallback)(
167
+ async ({ amount, to, onSuccess, onError }) => {
128
168
  setLoading(true);
129
169
  setSignature(null);
130
170
  setError(null);
171
+ if (!connected || !publicKey) {
172
+ setPendingPayment({ amount, to, onSuccess, onError });
173
+ setVisible(true);
174
+ return;
175
+ }
131
176
  try {
132
- if (!connected) {
133
- setVisible(true);
134
- await connect();
135
- }
136
- if (!publicKey) {
137
- throw new Error("Wallet not connected");
138
- }
139
- const recipient = new import_web32.PublicKey(to);
140
- const transaction = await buildUsdcTransfer({
141
- connection,
142
- fromWallet: publicKey,
143
- toWallet: recipient,
144
- amount
145
- });
146
- const sig = await sendTransaction(transaction, connection);
147
- await connection.confirmTransaction(sig, "confirmed");
148
- setSignature(sig);
149
- return sig;
177
+ const sig = await executePayment({ amount, to });
178
+ onSuccess?.(sig);
150
179
  } catch (err) {
151
180
  const normalized = err instanceof Error ? err : new Error("Payment failed");
152
181
  setError(normalized);
153
- throw normalized;
182
+ onError?.(normalized);
154
183
  } finally {
155
184
  setLoading(false);
156
185
  }
157
186
  },
158
- [connected, publicKey, sendTransaction, connection, connect, setVisible]
187
+ [connected, publicKey, executePayment, setVisible]
159
188
  );
189
+ (0, import_react3.useEffect)(() => {
190
+ if (!connected || !publicKey || !pendingPayment) return;
191
+ const payment = pendingPayment;
192
+ let cancelled = false;
193
+ async function resumePayment() {
194
+ try {
195
+ const sig = await executePayment(payment);
196
+ if (!cancelled) {
197
+ payment.onSuccess?.(sig);
198
+ setSignature(sig);
199
+ setPendingPayment(null);
200
+ }
201
+ } catch (err) {
202
+ if (!cancelled) {
203
+ const normalized = err instanceof Error ? err : new Error("Payment failed");
204
+ setError(normalized);
205
+ payment.onError?.(normalized);
206
+ setPendingPayment(null);
207
+ }
208
+ } finally {
209
+ if (!cancelled) {
210
+ setLoading(false);
211
+ }
212
+ }
213
+ }
214
+ resumePayment();
215
+ return () => {
216
+ cancelled = true;
217
+ };
218
+ }, [connected, publicKey, pendingPayment, executePayment]);
160
219
  return { pay, loading, error, signature };
161
220
  }
162
221
 
163
222
  // src/components/StablePay.tsx
223
+ function getResolvedTo(to, defaultTo) {
224
+ const resolved = to ?? defaultTo;
225
+ if (!resolved) {
226
+ throw new Error(
227
+ "StablePay: missing recipient address. Provide `to` on StablePay or StablePayProvider."
228
+ );
229
+ }
230
+ return resolved;
231
+ }
164
232
  function StablePay({
165
233
  amount,
166
234
  to,
@@ -169,24 +237,35 @@ function StablePay({
169
237
  onError
170
238
  }) {
171
239
  const { pay, loading } = useStablePay();
172
- if (!import_react3.default.isValidElement(children)) {
240
+ const { defaultTo } = useStablePayContext();
241
+ const resolvedTo = getResolvedTo(to, defaultTo);
242
+ if (!resolvedTo) {
243
+ throw new Error(
244
+ "StablePay: missing recipient address. Provide `to` on StablePay or StablePayProvider."
245
+ );
246
+ }
247
+ if (!import_react4.default.isValidElement(children)) {
173
248
  throw new Error("StablePay expects a single React element child.");
174
249
  }
175
250
  async function handleClick(event) {
176
251
  try {
177
252
  children.props.onClick?.(event);
178
253
  if (event.defaultPrevented) return;
179
- const signature = await pay({ amount, to });
180
- onSuccess?.({ signature, amount, to });
254
+ await pay({
255
+ amount,
256
+ to: resolvedTo,
257
+ onSuccess: (signature) => {
258
+ onSuccess?.({ signature, amount, to: resolvedTo });
259
+ },
260
+ onError
261
+ });
181
262
  } catch (err) {
182
263
  onError?.(err instanceof Error ? err : new Error("Payment failed"));
183
264
  }
184
265
  }
185
- return import_react3.default.cloneElement(children, {
266
+ return import_react4.default.cloneElement(children, {
186
267
  onClick: handleClick,
187
- disabled: Boolean(children.props.disabled) || loading,
188
- "aria-busy": loading,
189
- "data-stablepay-loading": loading ? "true" : "false"
268
+ disabled: Boolean(children.props.disabled) || loading
190
269
  });
191
270
  }
192
271
  // Annotate the CommonJS export names for ESM import in node:
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/provider/StablePayProvider.tsx","../src/components/StablePay.tsx","../src/hooks/useStablePay.ts","../src/solana/buildUsdcTransfer.ts","../src/solana/constants.ts","../src/solana/amount.ts"],"sourcesContent":["export { StablePayProvider } from \"./provider/StablePayProvider\";\nexport { StablePay } from \"./components/StablePay\";\nexport { useStablePay } from \"./hooks/useStablePay\";\nexport type {\n StablePayProps,\n StablePayProviderProps,\n StablePaySuccessPayload,\n StablePayPayArgs,\n UseStablePayResult,\n} from \"./types\";\n","import React, { useMemo } from \"react\";\nimport { ConnectionProvider, WalletProvider } from \"@solana/wallet-adapter-react\";\nimport { WalletModalProvider } from \"@solana/wallet-adapter-react-ui\";\nimport { PhantomWalletAdapter, SolflareWalletAdapter } from \"@solana/wallet-adapter-wallets\";\nimport type { StablePayProviderProps } from \"../types\";\n\nimport \"@solana/wallet-adapter-react-ui/styles.css\";\n\nexport function StablePayProvider({\n children,\n endpoint = \"https://api.mainnet-beta.solana.com\",\n}: StablePayProviderProps) {\n const wallets = useMemo(\n () => [new PhantomWalletAdapter(), new SolflareWalletAdapter()],\n []\n );\n\n return (\n <ConnectionProvider endpoint={endpoint}>\n <WalletProvider wallets={wallets} autoConnect>\n <WalletModalProvider>{children}</WalletModalProvider>\n </WalletProvider>\n </ConnectionProvider>\n );\n}\n","import React from \"react\";\nimport { useStablePay } from \"../hooks/useStablePay\";\nimport type { StablePayProps } from \"../types\";\n\ntype ClickableChildProps = {\n onClick?: (event: React.MouseEvent) => void;\n disabled?: boolean;\n \"aria-busy\"?: boolean;\n \"data-stablepay-loading\"?: string;\n};\n\nexport function StablePay({\n amount,\n to,\n children,\n onSuccess,\n onError,\n}: StablePayProps) {\n const { pay, loading } = useStablePay();\n\n if (!React.isValidElement<ClickableChildProps>(children)) {\n throw new Error(\"StablePay expects a single React element child.\");\n }\n\n async function handleClick(event: React.MouseEvent) {\n try {\n children.props.onClick?.(event);\n\n if (event.defaultPrevented) return;\n\n const signature = await pay({ amount, to });\n onSuccess?.({ signature, amount, to });\n } catch (err) {\n onError?.(err instanceof Error ? err : new Error(\"Payment failed\"));\n }\n }\n\n return React.cloneElement(children, {\n onClick: handleClick,\n disabled: Boolean(children.props.disabled) || loading,\n \"aria-busy\": loading,\n \"data-stablepay-loading\": loading ? \"true\" : \"false\",\n });\n}\n","import { useCallback, useState } from \"react\";\nimport { PublicKey } from \"@solana/web3.js\";\nimport { useConnection, useWallet } from \"@solana/wallet-adapter-react\";\nimport { useWalletModal } from \"@solana/wallet-adapter-react-ui\";\nimport { buildUsdcTransfer } from \"../solana/buildUsdcTransfer\";\nimport type { StablePayPayArgs, UseStablePayResult } from \"../types\";\n\nexport function useStablePay(): UseStablePayResult {\n const { connection } = useConnection();\n const { connected, publicKey, sendTransaction, connect } = useWallet();\n const { setVisible } = useWalletModal();\n\n const [loading, setLoading] = useState(false);\n const [signature, setSignature] = useState<string | null>(null);\n const [error, setError] = useState<Error | null>(null);\n\n const pay = useCallback(\n async ({ amount, to }: StablePayPayArgs) => {\n setLoading(true);\n setSignature(null);\n setError(null);\n\n try {\n if (!connected) {\n setVisible(true);\n await connect();\n }\n\n if (!publicKey) {\n throw new Error(\"Wallet not connected\");\n }\n\n const recipient = new PublicKey(to);\n const transaction = await buildUsdcTransfer({\n connection,\n fromWallet: publicKey,\n toWallet: recipient,\n amount,\n });\n\n const sig = await sendTransaction(transaction, connection);\n await connection.confirmTransaction(sig, \"confirmed\");\n\n setSignature(sig);\n return sig;\n } catch (err) {\n const normalized =\n err instanceof Error ? err : new Error(\"Payment failed\");\n setError(normalized);\n throw normalized;\n } finally {\n setLoading(false);\n }\n },\n [connected, publicKey, sendTransaction, connection, connect, setVisible],\n );\n\n return { pay, loading, error, signature };\n}\n","import { Connection, PublicKey, Transaction } from \"@solana/web3.js\";\nimport {\n TOKEN_PROGRAM_ID,\n createAssociatedTokenAccountInstruction,\n createTransferCheckedInstruction,\n getAssociatedTokenAddress,\n} from \"@solana/spl-token\";\nimport { toTokenBaseUnits } from \"./amount\";\nimport { USDC_DECIMALS, USDC_MINT } from \"./constants\";\n\ntype BuildUsdcTransferParams = {\n connection: Connection;\n fromWallet: PublicKey;\n toWallet: PublicKey;\n amount: number;\n};\n\nexport async function buildUsdcTransfer({\n connection,\n fromWallet,\n toWallet,\n amount,\n}: BuildUsdcTransferParams) {\n const mint = new PublicKey(USDC_MINT);\n const fromAta = await getAssociatedTokenAddress(mint, fromWallet);\n const toAta = await getAssociatedTokenAddress(mint, toWallet);\n\n const tx = new Transaction();\n const toAtaInfo = await connection.getAccountInfo(toAta);\n\n if (!toAtaInfo) {\n tx.add(\n createAssociatedTokenAccountInstruction(fromWallet, toAta, toWallet, mint)\n );\n }\n\n tx.add(\n createTransferCheckedInstruction(\n fromAta,\n mint,\n toAta,\n fromWallet,\n Number(toTokenBaseUnits(amount)),\n USDC_DECIMALS,\n [],\n TOKEN_PROGRAM_ID\n )\n );\n\n const latestBlockhash = await connection.getLatestBlockhash();\n tx.feePayer = fromWallet;\n tx.recentBlockhash = latestBlockhash.blockhash;\n\n return tx;\n}\n","export const USDC_MINT = \"EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v\";\nexport const USDC_DECIMALS = 6;\n","import { USDC_DECIMALS } from \"./constants\";\n\nexport function toTokenBaseUnits(amount: number): bigint {\n if (!Number.isFinite(amount) || amount <= 0) {\n throw new Error(\"Amount must be a positive number\");\n }\n\n const multiplier = 10 ** USDC_DECIMALS;\n return BigInt(Math.round(amount * multiplier));\n}\n"],"mappings":"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","names":["import_react","import_react","import_web3","import_wallet_adapter_react","import_wallet_adapter_react_ui","React"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/provider/StablePayProvider.tsx","../src/context/StablePayContext.ts","../src/components/StablePay.tsx","../src/hooks/useStablePay.ts","../src/solana/buildUsdcTransfer.ts","../src/solana/constants.ts","../src/solana/amount.ts"],"sourcesContent":["export { StablePayProvider } from \"./provider/StablePayProvider\";\nexport { StablePay } from \"./components/StablePay\";\nexport { useStablePay } from \"./hooks/useStablePay\";\nexport type {\n StablePayProps,\n StablePayProviderProps,\n StablePaySuccessPayload,\n StablePayPayArgs,\n UseStablePayResult,\n} from \"./types\";\n","import React, { useMemo } from \"react\";\nimport {\n ConnectionProvider,\n WalletProvider,\n} from \"@solana/wallet-adapter-react\";\nimport { WalletModalProvider } from \"@solana/wallet-adapter-react-ui\";\nimport {\n PhantomWalletAdapter,\n SolflareWalletAdapter,\n} from \"@solana/wallet-adapter-wallets\";\nimport type { StablePayProviderProps } from \"../types\";\nimport StablePayContext from \"../context/StablePayContext\";\n\nimport \"@solana/wallet-adapter-react-ui/styles.css\";\n\nconst DEFAULT_RPC =\n \"https://mainnet.helius-rpc.com/?api-key=a90079ee-e2b5-438e-9b1a-2ae7ba8c1417\";\n\nexport function StablePayProvider({\n children,\n endpoint,\n to,\n}: StablePayProviderProps) {\n const resolvedEndpoint = endpoint ?? DEFAULT_RPC;\n\n const wallets = useMemo(\n () => [new PhantomWalletAdapter(), new SolflareWalletAdapter()],\n [],\n );\n\n return (\n <StablePayContext.Provider value={{ defaultTo: to }}>\n <ConnectionProvider endpoint={resolvedEndpoint}>\n <WalletProvider wallets={wallets} autoConnect>\n <WalletModalProvider>{children}</WalletModalProvider>\n </WalletProvider>\n </ConnectionProvider>\n </StablePayContext.Provider>\n );\n}\n","import { createContext, useContext } from \"react\";\n\ntype StablePayContextValue = {\n defaultTo?: string;\n};\n\nconst StablePayContext = createContext<StablePayContextValue>({});\n\nexport function useStablePayContext() {\n return useContext(StablePayContext);\n}\n\nexport default StablePayContext;\n","import React from \"react\";\nimport { useStablePay } from \"../hooks/useStablePay\";\nimport { useStablePayContext } from \"../context/StablePayContext\";\nimport type { StablePayProps } from \"../types\";\n\ntype ClickableChildProps = {\n onClick?: (event: React.MouseEvent) => void;\n disabled?: boolean;\n};\n\nfunction getResolvedTo(to?: string, defaultTo?: string): string {\n const resolved = to ?? defaultTo;\n\n if (!resolved) {\n throw new Error(\n \"StablePay: missing recipient address. Provide `to` on StablePay or StablePayProvider.\",\n );\n }\n\n return resolved;\n}\n\nexport function StablePay({\n amount,\n to,\n children,\n onSuccess,\n onError,\n}: StablePayProps) {\n const { pay, loading } = useStablePay();\n const { defaultTo } = useStablePayContext();\n\n const resolvedTo = getResolvedTo(to, defaultTo);\n\n if (!resolvedTo) {\n throw new Error(\n \"StablePay: missing recipient address. Provide `to` on StablePay or StablePayProvider.\",\n );\n }\n\n if (!React.isValidElement<ClickableChildProps>(children)) {\n throw new Error(\"StablePay expects a single React element child.\");\n }\n\n async function handleClick(event: React.MouseEvent) {\n try {\n children.props.onClick?.(event);\n\n if (event.defaultPrevented) return;\n\n await pay({\n amount,\n to: resolvedTo,\n onSuccess: (signature) => {\n onSuccess?.({ signature, amount, to: resolvedTo });\n },\n onError,\n });\n } catch (err) {\n onError?.(err instanceof Error ? err : new Error(\"Payment failed\"));\n }\n }\n\n return React.cloneElement(children, {\n onClick: handleClick,\n disabled: Boolean(children.props.disabled) || loading,\n });\n}\n","import { useCallback, useEffect, useState } from \"react\";\nimport { PublicKey } from \"@solana/web3.js\";\nimport { useConnection, useWallet } from \"@solana/wallet-adapter-react\";\nimport { useWalletModal } from \"@solana/wallet-adapter-react-ui\";\nimport { buildUsdcTransfer } from \"../solana/buildUsdcTransfer\";\n\ntype PayArgs = {\n amount: number;\n to: string;\n onSuccess?: (signature: string) => void;\n onError?: (error: Error) => void;\n};\n\ntype PendingPayment = PayArgs;\n\nexport function useStablePay() {\n const { connection } = useConnection();\n const { connected, publicKey, sendTransaction } = useWallet();\n const { setVisible } = useWalletModal();\n\n const [loading, setLoading] = useState(false);\n const [signature, setSignature] = useState<string | null>(null);\n const [error, setError] = useState<Error | null>(null);\n const [pendingPayment, setPendingPayment] = useState<PendingPayment | null>(\n null,\n );\n\n const executePayment = useCallback(\n async ({ amount, to }: PayArgs) => {\n if (!publicKey) {\n throw new Error(\"Wallet not connected\");\n }\n\n let recipient: PublicKey;\n\n try {\n recipient = new PublicKey(to);\n } catch {\n throw new Error(\"Invalid recipient wallet address\");\n }\n\n const transaction = await buildUsdcTransfer({\n connection,\n fromWallet: publicKey,\n toWallet: recipient,\n amount,\n });\n\n const sig = await sendTransaction(transaction, connection);\n await connection.confirmTransaction(sig, \"confirmed\");\n\n setSignature(sig);\n return sig;\n },\n [publicKey, connection, sendTransaction],\n );\n\n const pay = useCallback(\n async ({ amount, to, onSuccess, onError }: PayArgs) => {\n setLoading(true);\n setSignature(null);\n setError(null);\n\n // Not connected → store intent + callbacks\n if (!connected || !publicKey) {\n setPendingPayment({ amount, to, onSuccess, onError });\n setVisible(true);\n return;\n }\n\n try {\n const sig = await executePayment({ amount, to });\n onSuccess?.(sig);\n } catch (err) {\n const normalized =\n err instanceof Error ? err : new Error(\"Payment failed\");\n setError(normalized);\n onError?.(normalized);\n } finally {\n setLoading(false);\n }\n },\n [connected, publicKey, executePayment, setVisible],\n );\n\n // Resume after wallet connects\n useEffect(() => {\n if (!connected || !publicKey || !pendingPayment) return;\n\n const payment = pendingPayment;\n\n let cancelled = false;\n\n async function resumePayment() {\n try {\n const sig = await executePayment(payment);\n\n if (!cancelled) {\n payment.onSuccess?.(sig);\n setSignature(sig);\n setPendingPayment(null);\n }\n } catch (err) {\n if (!cancelled) {\n const normalized =\n err instanceof Error ? err : new Error(\"Payment failed\");\n setError(normalized);\n payment.onError?.(normalized);\n setPendingPayment(null);\n }\n } finally {\n if (!cancelled) {\n setLoading(false);\n }\n }\n }\n\n resumePayment();\n\n return () => {\n cancelled = true;\n };\n }, [connected, publicKey, pendingPayment, executePayment]);\n\n return { pay, loading, error, signature };\n}\n","import { Connection, PublicKey, Transaction } from \"@solana/web3.js\";\nimport {\n TOKEN_PROGRAM_ID,\n createAssociatedTokenAccountInstruction,\n createTransferCheckedInstruction,\n getAssociatedTokenAddress,\n} from \"@solana/spl-token\";\nimport { toTokenBaseUnits } from \"./amount\";\nimport { USDC_DECIMALS, USDC_MINT } from \"./constants\";\n\ntype BuildUsdcTransferParams = {\n connection: Connection;\n fromWallet: PublicKey;\n toWallet: PublicKey;\n amount: number;\n};\n\nexport async function buildUsdcTransfer({\n connection,\n fromWallet,\n toWallet,\n amount,\n}: BuildUsdcTransferParams) {\n const mint = new PublicKey(USDC_MINT);\n const fromAta = await getAssociatedTokenAddress(mint, fromWallet);\n const toAta = await getAssociatedTokenAddress(mint, toWallet);\n\n const tx = new Transaction();\n const toAtaInfo = await connection.getAccountInfo(toAta);\n\n if (!toAtaInfo) {\n tx.add(\n createAssociatedTokenAccountInstruction(fromWallet, toAta, toWallet, mint)\n );\n }\n\n tx.add(\n createTransferCheckedInstruction(\n fromAta,\n mint,\n toAta,\n fromWallet,\n Number(toTokenBaseUnits(amount)),\n USDC_DECIMALS,\n [],\n TOKEN_PROGRAM_ID\n )\n );\n\n const latestBlockhash = await connection.getLatestBlockhash();\n tx.feePayer = fromWallet;\n tx.recentBlockhash = latestBlockhash.blockhash;\n\n return tx;\n}\n","export const USDC_MINT = \"EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v\";\nexport const USDC_DECIMALS = 6;\n","import { USDC_DECIMALS } from \"./constants\";\n\nexport function toTokenBaseUnits(amount: number): bigint {\n if (!Number.isFinite(amount) || amount <= 0) {\n throw new Error(\"Amount must be a positive number\");\n }\n\n const multiplier = 10 ** USDC_DECIMALS;\n return BigInt(Math.round(amount * multiplier));\n}\n"],"mappings":"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","names":["import_react","import_react","import_react","import_web3","import_wallet_adapter_react","import_wallet_adapter_react_ui","React"]}
package/dist/index.mjs CHANGED
@@ -1,26 +1,45 @@
1
1
  // src/provider/StablePayProvider.tsx
2
2
  import { useMemo } from "react";
3
- import { ConnectionProvider, WalletProvider } from "@solana/wallet-adapter-react";
3
+ import {
4
+ ConnectionProvider,
5
+ WalletProvider
6
+ } from "@solana/wallet-adapter-react";
4
7
  import { WalletModalProvider } from "@solana/wallet-adapter-react-ui";
5
- import { PhantomWalletAdapter, SolflareWalletAdapter } from "@solana/wallet-adapter-wallets";
8
+ import {
9
+ PhantomWalletAdapter,
10
+ SolflareWalletAdapter
11
+ } from "@solana/wallet-adapter-wallets";
12
+
13
+ // src/context/StablePayContext.ts
14
+ import { createContext, useContext } from "react";
15
+ var StablePayContext = createContext({});
16
+ function useStablePayContext() {
17
+ return useContext(StablePayContext);
18
+ }
19
+ var StablePayContext_default = StablePayContext;
20
+
21
+ // src/provider/StablePayProvider.tsx
6
22
  import "@solana/wallet-adapter-react-ui/styles.css";
7
23
  import { jsx } from "react/jsx-runtime";
24
+ var DEFAULT_RPC = "https://mainnet.helius-rpc.com/?api-key=a90079ee-e2b5-438e-9b1a-2ae7ba8c1417";
8
25
  function StablePayProvider({
9
26
  children,
10
- endpoint = "https://api.mainnet-beta.solana.com"
27
+ endpoint,
28
+ to
11
29
  }) {
30
+ const resolvedEndpoint = endpoint ?? DEFAULT_RPC;
12
31
  const wallets = useMemo(
13
32
  () => [new PhantomWalletAdapter(), new SolflareWalletAdapter()],
14
33
  []
15
34
  );
16
- return /* @__PURE__ */ jsx(ConnectionProvider, { endpoint, children: /* @__PURE__ */ jsx(WalletProvider, { wallets, autoConnect: true, children: /* @__PURE__ */ jsx(WalletModalProvider, { children }) }) });
35
+ return /* @__PURE__ */ jsx(StablePayContext_default.Provider, { value: { defaultTo: to }, children: /* @__PURE__ */ jsx(ConnectionProvider, { endpoint: resolvedEndpoint, children: /* @__PURE__ */ jsx(WalletProvider, { wallets, autoConnect: true, children: /* @__PURE__ */ jsx(WalletModalProvider, { children }) }) }) });
17
36
  }
18
37
 
19
38
  // src/components/StablePay.tsx
20
39
  import React2 from "react";
21
40
 
22
41
  // src/hooks/useStablePay.ts
23
- import { useCallback, useState } from "react";
42
+ import { useCallback, useEffect, useState } from "react";
24
43
  import { PublicKey as PublicKey2 } from "@solana/web3.js";
25
44
  import { useConnection, useWallet } from "@solana/wallet-adapter-react";
26
45
  import { useWalletModal } from "@solana/wallet-adapter-react-ui";
@@ -85,49 +104,104 @@ async function buildUsdcTransfer({
85
104
  // src/hooks/useStablePay.ts
86
105
  function useStablePay() {
87
106
  const { connection } = useConnection();
88
- const { connected, publicKey, sendTransaction, connect } = useWallet();
107
+ const { connected, publicKey, sendTransaction } = useWallet();
89
108
  const { setVisible } = useWalletModal();
90
109
  const [loading, setLoading] = useState(false);
91
110
  const [signature, setSignature] = useState(null);
92
111
  const [error, setError] = useState(null);
93
- const pay = useCallback(
112
+ const [pendingPayment, setPendingPayment] = useState(
113
+ null
114
+ );
115
+ const executePayment = useCallback(
94
116
  async ({ amount, to }) => {
117
+ if (!publicKey) {
118
+ throw new Error("Wallet not connected");
119
+ }
120
+ let recipient;
121
+ try {
122
+ recipient = new PublicKey2(to);
123
+ } catch {
124
+ throw new Error("Invalid recipient wallet address");
125
+ }
126
+ const transaction = await buildUsdcTransfer({
127
+ connection,
128
+ fromWallet: publicKey,
129
+ toWallet: recipient,
130
+ amount
131
+ });
132
+ const sig = await sendTransaction(transaction, connection);
133
+ await connection.confirmTransaction(sig, "confirmed");
134
+ setSignature(sig);
135
+ return sig;
136
+ },
137
+ [publicKey, connection, sendTransaction]
138
+ );
139
+ const pay = useCallback(
140
+ async ({ amount, to, onSuccess, onError }) => {
95
141
  setLoading(true);
96
142
  setSignature(null);
97
143
  setError(null);
144
+ if (!connected || !publicKey) {
145
+ setPendingPayment({ amount, to, onSuccess, onError });
146
+ setVisible(true);
147
+ return;
148
+ }
98
149
  try {
99
- if (!connected) {
100
- setVisible(true);
101
- await connect();
102
- }
103
- if (!publicKey) {
104
- throw new Error("Wallet not connected");
105
- }
106
- const recipient = new PublicKey2(to);
107
- const transaction = await buildUsdcTransfer({
108
- connection,
109
- fromWallet: publicKey,
110
- toWallet: recipient,
111
- amount
112
- });
113
- const sig = await sendTransaction(transaction, connection);
114
- await connection.confirmTransaction(sig, "confirmed");
115
- setSignature(sig);
116
- return sig;
150
+ const sig = await executePayment({ amount, to });
151
+ onSuccess?.(sig);
117
152
  } catch (err) {
118
153
  const normalized = err instanceof Error ? err : new Error("Payment failed");
119
154
  setError(normalized);
120
- throw normalized;
155
+ onError?.(normalized);
121
156
  } finally {
122
157
  setLoading(false);
123
158
  }
124
159
  },
125
- [connected, publicKey, sendTransaction, connection, connect, setVisible]
160
+ [connected, publicKey, executePayment, setVisible]
126
161
  );
162
+ useEffect(() => {
163
+ if (!connected || !publicKey || !pendingPayment) return;
164
+ const payment = pendingPayment;
165
+ let cancelled = false;
166
+ async function resumePayment() {
167
+ try {
168
+ const sig = await executePayment(payment);
169
+ if (!cancelled) {
170
+ payment.onSuccess?.(sig);
171
+ setSignature(sig);
172
+ setPendingPayment(null);
173
+ }
174
+ } catch (err) {
175
+ if (!cancelled) {
176
+ const normalized = err instanceof Error ? err : new Error("Payment failed");
177
+ setError(normalized);
178
+ payment.onError?.(normalized);
179
+ setPendingPayment(null);
180
+ }
181
+ } finally {
182
+ if (!cancelled) {
183
+ setLoading(false);
184
+ }
185
+ }
186
+ }
187
+ resumePayment();
188
+ return () => {
189
+ cancelled = true;
190
+ };
191
+ }, [connected, publicKey, pendingPayment, executePayment]);
127
192
  return { pay, loading, error, signature };
128
193
  }
129
194
 
130
195
  // src/components/StablePay.tsx
196
+ function getResolvedTo(to, defaultTo) {
197
+ const resolved = to ?? defaultTo;
198
+ if (!resolved) {
199
+ throw new Error(
200
+ "StablePay: missing recipient address. Provide `to` on StablePay or StablePayProvider."
201
+ );
202
+ }
203
+ return resolved;
204
+ }
131
205
  function StablePay({
132
206
  amount,
133
207
  to,
@@ -136,6 +210,13 @@ function StablePay({
136
210
  onError
137
211
  }) {
138
212
  const { pay, loading } = useStablePay();
213
+ const { defaultTo } = useStablePayContext();
214
+ const resolvedTo = getResolvedTo(to, defaultTo);
215
+ if (!resolvedTo) {
216
+ throw new Error(
217
+ "StablePay: missing recipient address. Provide `to` on StablePay or StablePayProvider."
218
+ );
219
+ }
139
220
  if (!React2.isValidElement(children)) {
140
221
  throw new Error("StablePay expects a single React element child.");
141
222
  }
@@ -143,17 +224,21 @@ function StablePay({
143
224
  try {
144
225
  children.props.onClick?.(event);
145
226
  if (event.defaultPrevented) return;
146
- const signature = await pay({ amount, to });
147
- onSuccess?.({ signature, amount, to });
227
+ await pay({
228
+ amount,
229
+ to: resolvedTo,
230
+ onSuccess: (signature) => {
231
+ onSuccess?.({ signature, amount, to: resolvedTo });
232
+ },
233
+ onError
234
+ });
148
235
  } catch (err) {
149
236
  onError?.(err instanceof Error ? err : new Error("Payment failed"));
150
237
  }
151
238
  }
152
239
  return React2.cloneElement(children, {
153
240
  onClick: handleClick,
154
- disabled: Boolean(children.props.disabled) || loading,
155
- "aria-busy": loading,
156
- "data-stablepay-loading": loading ? "true" : "false"
241
+ disabled: Boolean(children.props.disabled) || loading
157
242
  });
158
243
  }
159
244
  export {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/provider/StablePayProvider.tsx","../src/components/StablePay.tsx","../src/hooks/useStablePay.ts","../src/solana/buildUsdcTransfer.ts","../src/solana/constants.ts","../src/solana/amount.ts"],"sourcesContent":["import React, { useMemo } from \"react\";\nimport { ConnectionProvider, WalletProvider } from \"@solana/wallet-adapter-react\";\nimport { WalletModalProvider } from \"@solana/wallet-adapter-react-ui\";\nimport { PhantomWalletAdapter, SolflareWalletAdapter } from \"@solana/wallet-adapter-wallets\";\nimport type { StablePayProviderProps } from \"../types\";\n\nimport \"@solana/wallet-adapter-react-ui/styles.css\";\n\nexport function StablePayProvider({\n children,\n endpoint = \"https://api.mainnet-beta.solana.com\",\n}: StablePayProviderProps) {\n const wallets = useMemo(\n () => [new PhantomWalletAdapter(), new SolflareWalletAdapter()],\n []\n );\n\n return (\n <ConnectionProvider endpoint={endpoint}>\n <WalletProvider wallets={wallets} autoConnect>\n <WalletModalProvider>{children}</WalletModalProvider>\n </WalletProvider>\n </ConnectionProvider>\n );\n}\n","import React from \"react\";\nimport { useStablePay } from \"../hooks/useStablePay\";\nimport type { StablePayProps } from \"../types\";\n\ntype ClickableChildProps = {\n onClick?: (event: React.MouseEvent) => void;\n disabled?: boolean;\n \"aria-busy\"?: boolean;\n \"data-stablepay-loading\"?: string;\n};\n\nexport function StablePay({\n amount,\n to,\n children,\n onSuccess,\n onError,\n}: StablePayProps) {\n const { pay, loading } = useStablePay();\n\n if (!React.isValidElement<ClickableChildProps>(children)) {\n throw new Error(\"StablePay expects a single React element child.\");\n }\n\n async function handleClick(event: React.MouseEvent) {\n try {\n children.props.onClick?.(event);\n\n if (event.defaultPrevented) return;\n\n const signature = await pay({ amount, to });\n onSuccess?.({ signature, amount, to });\n } catch (err) {\n onError?.(err instanceof Error ? err : new Error(\"Payment failed\"));\n }\n }\n\n return React.cloneElement(children, {\n onClick: handleClick,\n disabled: Boolean(children.props.disabled) || loading,\n \"aria-busy\": loading,\n \"data-stablepay-loading\": loading ? \"true\" : \"false\",\n });\n}\n","import { useCallback, useState } from \"react\";\nimport { PublicKey } from \"@solana/web3.js\";\nimport { useConnection, useWallet } from \"@solana/wallet-adapter-react\";\nimport { useWalletModal } from \"@solana/wallet-adapter-react-ui\";\nimport { buildUsdcTransfer } from \"../solana/buildUsdcTransfer\";\nimport type { StablePayPayArgs, UseStablePayResult } from \"../types\";\n\nexport function useStablePay(): UseStablePayResult {\n const { connection } = useConnection();\n const { connected, publicKey, sendTransaction, connect } = useWallet();\n const { setVisible } = useWalletModal();\n\n const [loading, setLoading] = useState(false);\n const [signature, setSignature] = useState<string | null>(null);\n const [error, setError] = useState<Error | null>(null);\n\n const pay = useCallback(\n async ({ amount, to }: StablePayPayArgs) => {\n setLoading(true);\n setSignature(null);\n setError(null);\n\n try {\n if (!connected) {\n setVisible(true);\n await connect();\n }\n\n if (!publicKey) {\n throw new Error(\"Wallet not connected\");\n }\n\n const recipient = new PublicKey(to);\n const transaction = await buildUsdcTransfer({\n connection,\n fromWallet: publicKey,\n toWallet: recipient,\n amount,\n });\n\n const sig = await sendTransaction(transaction, connection);\n await connection.confirmTransaction(sig, \"confirmed\");\n\n setSignature(sig);\n return sig;\n } catch (err) {\n const normalized =\n err instanceof Error ? err : new Error(\"Payment failed\");\n setError(normalized);\n throw normalized;\n } finally {\n setLoading(false);\n }\n },\n [connected, publicKey, sendTransaction, connection, connect, setVisible],\n );\n\n return { pay, loading, error, signature };\n}\n","import { Connection, PublicKey, Transaction } from \"@solana/web3.js\";\nimport {\n TOKEN_PROGRAM_ID,\n createAssociatedTokenAccountInstruction,\n createTransferCheckedInstruction,\n getAssociatedTokenAddress,\n} from \"@solana/spl-token\";\nimport { toTokenBaseUnits } from \"./amount\";\nimport { USDC_DECIMALS, USDC_MINT } from \"./constants\";\n\ntype BuildUsdcTransferParams = {\n connection: Connection;\n fromWallet: PublicKey;\n toWallet: PublicKey;\n amount: number;\n};\n\nexport async function buildUsdcTransfer({\n connection,\n fromWallet,\n toWallet,\n amount,\n}: BuildUsdcTransferParams) {\n const mint = new PublicKey(USDC_MINT);\n const fromAta = await getAssociatedTokenAddress(mint, fromWallet);\n const toAta = await getAssociatedTokenAddress(mint, toWallet);\n\n const tx = new Transaction();\n const toAtaInfo = await connection.getAccountInfo(toAta);\n\n if (!toAtaInfo) {\n tx.add(\n createAssociatedTokenAccountInstruction(fromWallet, toAta, toWallet, mint)\n );\n }\n\n tx.add(\n createTransferCheckedInstruction(\n fromAta,\n mint,\n toAta,\n fromWallet,\n Number(toTokenBaseUnits(amount)),\n USDC_DECIMALS,\n [],\n TOKEN_PROGRAM_ID\n )\n );\n\n const latestBlockhash = await connection.getLatestBlockhash();\n tx.feePayer = fromWallet;\n tx.recentBlockhash = latestBlockhash.blockhash;\n\n return tx;\n}\n","export const USDC_MINT = \"EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v\";\nexport const USDC_DECIMALS = 6;\n","import { USDC_DECIMALS } from \"./constants\";\n\nexport function toTokenBaseUnits(amount: number): bigint {\n if (!Number.isFinite(amount) || amount <= 0) {\n throw new Error(\"Amount must be a positive number\");\n }\n\n const multiplier = 10 ** USDC_DECIMALS;\n return BigInt(Math.round(amount * multiplier));\n}\n"],"mappings":"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","names":["React","PublicKey","PublicKey","React"]}
1
+ {"version":3,"sources":["../src/provider/StablePayProvider.tsx","../src/context/StablePayContext.ts","../src/components/StablePay.tsx","../src/hooks/useStablePay.ts","../src/solana/buildUsdcTransfer.ts","../src/solana/constants.ts","../src/solana/amount.ts"],"sourcesContent":["import React, { useMemo } from \"react\";\nimport {\n ConnectionProvider,\n WalletProvider,\n} from \"@solana/wallet-adapter-react\";\nimport { WalletModalProvider } from \"@solana/wallet-adapter-react-ui\";\nimport {\n PhantomWalletAdapter,\n SolflareWalletAdapter,\n} from \"@solana/wallet-adapter-wallets\";\nimport type { StablePayProviderProps } from \"../types\";\nimport StablePayContext from \"../context/StablePayContext\";\n\nimport \"@solana/wallet-adapter-react-ui/styles.css\";\n\nconst DEFAULT_RPC =\n \"https://mainnet.helius-rpc.com/?api-key=a90079ee-e2b5-438e-9b1a-2ae7ba8c1417\";\n\nexport function StablePayProvider({\n children,\n endpoint,\n to,\n}: StablePayProviderProps) {\n const resolvedEndpoint = endpoint ?? DEFAULT_RPC;\n\n const wallets = useMemo(\n () => [new PhantomWalletAdapter(), new SolflareWalletAdapter()],\n [],\n );\n\n return (\n <StablePayContext.Provider value={{ defaultTo: to }}>\n <ConnectionProvider endpoint={resolvedEndpoint}>\n <WalletProvider wallets={wallets} autoConnect>\n <WalletModalProvider>{children}</WalletModalProvider>\n </WalletProvider>\n </ConnectionProvider>\n </StablePayContext.Provider>\n );\n}\n","import { createContext, useContext } from \"react\";\n\ntype StablePayContextValue = {\n defaultTo?: string;\n};\n\nconst StablePayContext = createContext<StablePayContextValue>({});\n\nexport function useStablePayContext() {\n return useContext(StablePayContext);\n}\n\nexport default StablePayContext;\n","import React from \"react\";\nimport { useStablePay } from \"../hooks/useStablePay\";\nimport { useStablePayContext } from \"../context/StablePayContext\";\nimport type { StablePayProps } from \"../types\";\n\ntype ClickableChildProps = {\n onClick?: (event: React.MouseEvent) => void;\n disabled?: boolean;\n};\n\nfunction getResolvedTo(to?: string, defaultTo?: string): string {\n const resolved = to ?? defaultTo;\n\n if (!resolved) {\n throw new Error(\n \"StablePay: missing recipient address. Provide `to` on StablePay or StablePayProvider.\",\n );\n }\n\n return resolved;\n}\n\nexport function StablePay({\n amount,\n to,\n children,\n onSuccess,\n onError,\n}: StablePayProps) {\n const { pay, loading } = useStablePay();\n const { defaultTo } = useStablePayContext();\n\n const resolvedTo = getResolvedTo(to, defaultTo);\n\n if (!resolvedTo) {\n throw new Error(\n \"StablePay: missing recipient address. Provide `to` on StablePay or StablePayProvider.\",\n );\n }\n\n if (!React.isValidElement<ClickableChildProps>(children)) {\n throw new Error(\"StablePay expects a single React element child.\");\n }\n\n async function handleClick(event: React.MouseEvent) {\n try {\n children.props.onClick?.(event);\n\n if (event.defaultPrevented) return;\n\n await pay({\n amount,\n to: resolvedTo,\n onSuccess: (signature) => {\n onSuccess?.({ signature, amount, to: resolvedTo });\n },\n onError,\n });\n } catch (err) {\n onError?.(err instanceof Error ? err : new Error(\"Payment failed\"));\n }\n }\n\n return React.cloneElement(children, {\n onClick: handleClick,\n disabled: Boolean(children.props.disabled) || loading,\n });\n}\n","import { useCallback, useEffect, useState } from \"react\";\nimport { PublicKey } from \"@solana/web3.js\";\nimport { useConnection, useWallet } from \"@solana/wallet-adapter-react\";\nimport { useWalletModal } from \"@solana/wallet-adapter-react-ui\";\nimport { buildUsdcTransfer } from \"../solana/buildUsdcTransfer\";\n\ntype PayArgs = {\n amount: number;\n to: string;\n onSuccess?: (signature: string) => void;\n onError?: (error: Error) => void;\n};\n\ntype PendingPayment = PayArgs;\n\nexport function useStablePay() {\n const { connection } = useConnection();\n const { connected, publicKey, sendTransaction } = useWallet();\n const { setVisible } = useWalletModal();\n\n const [loading, setLoading] = useState(false);\n const [signature, setSignature] = useState<string | null>(null);\n const [error, setError] = useState<Error | null>(null);\n const [pendingPayment, setPendingPayment] = useState<PendingPayment | null>(\n null,\n );\n\n const executePayment = useCallback(\n async ({ amount, to }: PayArgs) => {\n if (!publicKey) {\n throw new Error(\"Wallet not connected\");\n }\n\n let recipient: PublicKey;\n\n try {\n recipient = new PublicKey(to);\n } catch {\n throw new Error(\"Invalid recipient wallet address\");\n }\n\n const transaction = await buildUsdcTransfer({\n connection,\n fromWallet: publicKey,\n toWallet: recipient,\n amount,\n });\n\n const sig = await sendTransaction(transaction, connection);\n await connection.confirmTransaction(sig, \"confirmed\");\n\n setSignature(sig);\n return sig;\n },\n [publicKey, connection, sendTransaction],\n );\n\n const pay = useCallback(\n async ({ amount, to, onSuccess, onError }: PayArgs) => {\n setLoading(true);\n setSignature(null);\n setError(null);\n\n // Not connected → store intent + callbacks\n if (!connected || !publicKey) {\n setPendingPayment({ amount, to, onSuccess, onError });\n setVisible(true);\n return;\n }\n\n try {\n const sig = await executePayment({ amount, to });\n onSuccess?.(sig);\n } catch (err) {\n const normalized =\n err instanceof Error ? err : new Error(\"Payment failed\");\n setError(normalized);\n onError?.(normalized);\n } finally {\n setLoading(false);\n }\n },\n [connected, publicKey, executePayment, setVisible],\n );\n\n // Resume after wallet connects\n useEffect(() => {\n if (!connected || !publicKey || !pendingPayment) return;\n\n const payment = pendingPayment;\n\n let cancelled = false;\n\n async function resumePayment() {\n try {\n const sig = await executePayment(payment);\n\n if (!cancelled) {\n payment.onSuccess?.(sig);\n setSignature(sig);\n setPendingPayment(null);\n }\n } catch (err) {\n if (!cancelled) {\n const normalized =\n err instanceof Error ? err : new Error(\"Payment failed\");\n setError(normalized);\n payment.onError?.(normalized);\n setPendingPayment(null);\n }\n } finally {\n if (!cancelled) {\n setLoading(false);\n }\n }\n }\n\n resumePayment();\n\n return () => {\n cancelled = true;\n };\n }, [connected, publicKey, pendingPayment, executePayment]);\n\n return { pay, loading, error, signature };\n}\n","import { Connection, PublicKey, Transaction } from \"@solana/web3.js\";\nimport {\n TOKEN_PROGRAM_ID,\n createAssociatedTokenAccountInstruction,\n createTransferCheckedInstruction,\n getAssociatedTokenAddress,\n} from \"@solana/spl-token\";\nimport { toTokenBaseUnits } from \"./amount\";\nimport { USDC_DECIMALS, USDC_MINT } from \"./constants\";\n\ntype BuildUsdcTransferParams = {\n connection: Connection;\n fromWallet: PublicKey;\n toWallet: PublicKey;\n amount: number;\n};\n\nexport async function buildUsdcTransfer({\n connection,\n fromWallet,\n toWallet,\n amount,\n}: BuildUsdcTransferParams) {\n const mint = new PublicKey(USDC_MINT);\n const fromAta = await getAssociatedTokenAddress(mint, fromWallet);\n const toAta = await getAssociatedTokenAddress(mint, toWallet);\n\n const tx = new Transaction();\n const toAtaInfo = await connection.getAccountInfo(toAta);\n\n if (!toAtaInfo) {\n tx.add(\n createAssociatedTokenAccountInstruction(fromWallet, toAta, toWallet, mint)\n );\n }\n\n tx.add(\n createTransferCheckedInstruction(\n fromAta,\n mint,\n toAta,\n fromWallet,\n Number(toTokenBaseUnits(amount)),\n USDC_DECIMALS,\n [],\n TOKEN_PROGRAM_ID\n )\n );\n\n const latestBlockhash = await connection.getLatestBlockhash();\n tx.feePayer = fromWallet;\n tx.recentBlockhash = latestBlockhash.blockhash;\n\n return tx;\n}\n","export const USDC_MINT = \"EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v\";\nexport const USDC_DECIMALS = 6;\n","import { USDC_DECIMALS } from \"./constants\";\n\nexport function toTokenBaseUnits(amount: number): bigint {\n if (!Number.isFinite(amount) || amount <= 0) {\n throw new Error(\"Amount must be a positive number\");\n }\n\n const multiplier = 10 ** USDC_DECIMALS;\n return BigInt(Math.round(amount * multiplier));\n}\n"],"mappings":"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","names":["React","PublicKey","PublicKey","React"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@futurekode/stablepay-react",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Headless React wrapper for USDC payments on Solana",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",