@futurekode/stablepay-react 0.1.0 → 0.3.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
@@ -5,19 +5,28 @@ type StablePaySuccessPayload = {
5
5
  signature: string;
6
6
  amount: number;
7
7
  to: string;
8
+ reference?: string;
8
9
  };
9
10
  type StablePayPayArgs = {
10
11
  amount: number;
11
12
  to: string;
13
+ reference?: string;
14
+ onSuccess?: (payload: StablePaySuccessPayload) => void;
15
+ onError?: (error: Error) => void;
12
16
  };
13
17
  type StablePayProviderProps = {
14
18
  children: React.ReactNode;
15
19
  endpoint?: string;
20
+ to?: string;
16
21
  };
17
22
  type StablePayProps = {
18
23
  amount: number;
19
- to: string;
20
- children: React.ReactElement;
24
+ to?: string;
25
+ reference?: string;
26
+ children: React.ReactElement<{
27
+ onClick?: (event: React.MouseEvent) => void;
28
+ disabled?: boolean;
29
+ }>;
21
30
  onSuccess?: (payload: StablePaySuccessPayload) => void;
22
31
  onError?: (error: Error) => void;
23
32
  };
@@ -28,16 +37,18 @@ type UseStablePayResult = {
28
37
  signature: string | null;
29
38
  };
30
39
 
31
- declare function StablePayProvider({ children, endpoint, }: StablePayProviderProps): react_jsx_runtime.JSX.Element;
40
+ declare function StablePayProvider({ children, endpoint, to, }: StablePayProviderProps): react_jsx_runtime.JSX.Element;
32
41
 
33
- type ClickableChildProps = {
42
+ declare function StablePay({ amount, to, reference, children, onSuccess, onError, }: StablePayProps): React.ReactElement<{
34
43
  onClick?: (event: React.MouseEvent) => void;
35
44
  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>>;
45
+ }, string | React.JSXElementConstructor<any>>;
40
46
 
41
- declare function useStablePay(): UseStablePayResult;
47
+ declare function useStablePay(): {
48
+ pay: ({ amount, to, reference, onSuccess, onError }: StablePayPayArgs) => Promise<void>;
49
+ loading: boolean;
50
+ error: Error | null;
51
+ signature: string | null;
52
+ };
42
53
 
43
54
  export { StablePay, type StablePayPayArgs, type StablePayProps, StablePayProvider, type StablePayProviderProps, type StablePaySuccessPayload, type UseStablePayResult, useStablePay };
package/dist/index.d.ts CHANGED
@@ -5,19 +5,28 @@ type StablePaySuccessPayload = {
5
5
  signature: string;
6
6
  amount: number;
7
7
  to: string;
8
+ reference?: string;
8
9
  };
9
10
  type StablePayPayArgs = {
10
11
  amount: number;
11
12
  to: string;
13
+ reference?: string;
14
+ onSuccess?: (payload: StablePaySuccessPayload) => void;
15
+ onError?: (error: Error) => void;
12
16
  };
13
17
  type StablePayProviderProps = {
14
18
  children: React.ReactNode;
15
19
  endpoint?: string;
20
+ to?: string;
16
21
  };
17
22
  type StablePayProps = {
18
23
  amount: number;
19
- to: string;
20
- children: React.ReactElement;
24
+ to?: string;
25
+ reference?: string;
26
+ children: React.ReactElement<{
27
+ onClick?: (event: React.MouseEvent) => void;
28
+ disabled?: boolean;
29
+ }>;
21
30
  onSuccess?: (payload: StablePaySuccessPayload) => void;
22
31
  onError?: (error: Error) => void;
23
32
  };
@@ -28,16 +37,18 @@ type UseStablePayResult = {
28
37
  signature: string | null;
29
38
  };
30
39
 
31
- declare function StablePayProvider({ children, endpoint, }: StablePayProviderProps): react_jsx_runtime.JSX.Element;
40
+ declare function StablePayProvider({ children, endpoint, to, }: StablePayProviderProps): react_jsx_runtime.JSX.Element;
32
41
 
33
- type ClickableChildProps = {
42
+ declare function StablePay({ amount, to, reference, children, onSuccess, onError, }: StablePayProps): React.ReactElement<{
34
43
  onClick?: (event: React.MouseEvent) => void;
35
44
  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>>;
45
+ }, string | React.JSXElementConstructor<any>>;
40
46
 
41
- declare function useStablePay(): UseStablePayResult;
47
+ declare function useStablePay(): {
48
+ pay: ({ amount, to, reference, onSuccess, onError }: StablePayPayArgs) => Promise<void>;
49
+ loading: boolean;
50
+ error: Error | null;
51
+ signature: string | null;
52
+ };
42
53
 
43
54
  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,75 +131,151 @@ 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, reference, onSuccess, onError }) => {
128
168
  setLoading(true);
129
169
  setSignature(null);
130
170
  setError(null);
171
+ if (!connected || !publicKey) {
172
+ setPendingPayment({ amount, to, reference, 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
177
+ const sig = await executePayment({ amount, to });
178
+ onSuccess?.({
179
+ signature: sig,
180
+ amount,
181
+ to,
182
+ reference
145
183
  });
146
- const sig = await sendTransaction(transaction, connection);
147
- await connection.confirmTransaction(sig, "confirmed");
148
- setSignature(sig);
149
- return sig;
150
184
  } catch (err) {
151
185
  const normalized = err instanceof Error ? err : new Error("Payment failed");
152
186
  setError(normalized);
153
- throw normalized;
187
+ onError?.(normalized);
154
188
  } finally {
155
189
  setLoading(false);
156
190
  }
157
191
  },
158
- [connected, publicKey, sendTransaction, connection, connect, setVisible]
192
+ [connected, publicKey, executePayment, setVisible]
159
193
  );
194
+ (0, import_react3.useEffect)(() => {
195
+ if (!connected || !publicKey || !pendingPayment) return;
196
+ const payment = pendingPayment;
197
+ let cancelled = false;
198
+ async function resumePayment() {
199
+ try {
200
+ const sig = await executePayment(payment);
201
+ if (!cancelled) {
202
+ payment.onSuccess?.({
203
+ signature: sig,
204
+ amount: payment.amount,
205
+ to: payment.to,
206
+ reference: payment.reference
207
+ });
208
+ setSignature(sig);
209
+ setPendingPayment(null);
210
+ }
211
+ } catch (err) {
212
+ if (!cancelled) {
213
+ const normalized = err instanceof Error ? err : new Error("Payment failed");
214
+ setError(normalized);
215
+ payment.onError?.(normalized);
216
+ setPendingPayment(null);
217
+ }
218
+ } finally {
219
+ if (!cancelled) {
220
+ setLoading(false);
221
+ }
222
+ }
223
+ }
224
+ resumePayment();
225
+ return () => {
226
+ cancelled = true;
227
+ };
228
+ }, [connected, publicKey, pendingPayment, executePayment]);
160
229
  return { pay, loading, error, signature };
161
230
  }
162
231
 
163
232
  // src/components/StablePay.tsx
233
+ function getResolvedTo(to, defaultTo) {
234
+ const resolved = to ?? defaultTo;
235
+ if (!resolved) {
236
+ throw new Error(
237
+ "StablePay: missing recipient address. Provide `to` on StablePay or StablePayProvider."
238
+ );
239
+ }
240
+ return resolved;
241
+ }
164
242
  function StablePay({
165
243
  amount,
166
244
  to,
245
+ reference,
167
246
  children,
168
247
  onSuccess,
169
248
  onError
170
249
  }) {
171
250
  const { pay, loading } = useStablePay();
172
- if (!import_react3.default.isValidElement(children)) {
251
+ const { defaultTo } = useStablePayContext();
252
+ const resolvedTo = getResolvedTo(to, defaultTo);
253
+ if (!resolvedTo) {
254
+ throw new Error(
255
+ "StablePay: missing recipient address. Provide `to` on StablePay or StablePayProvider."
256
+ );
257
+ }
258
+ if (!import_react4.default.isValidElement(children)) {
173
259
  throw new Error("StablePay expects a single React element child.");
174
260
  }
175
261
  async function handleClick(event) {
176
262
  try {
177
263
  children.props.onClick?.(event);
178
264
  if (event.defaultPrevented) return;
179
- const signature = await pay({ amount, to });
180
- onSuccess?.({ signature, amount, to });
265
+ await pay({
266
+ amount,
267
+ to: resolvedTo,
268
+ reference,
269
+ onSuccess,
270
+ onError
271
+ });
181
272
  } catch (err) {
182
273
  onError?.(err instanceof Error ? err : new Error("Payment failed"));
183
274
  }
184
275
  }
185
- return import_react3.default.cloneElement(children, {
276
+ return import_react4.default.cloneElement(children, {
186
277
  onClick: handleClick,
187
- disabled: Boolean(children.props.disabled) || loading,
188
- "aria-busy": loading,
189
- "data-stablepay-loading": loading ? "true" : "false"
278
+ disabled: Boolean(children.props.disabled) || loading
190
279
  });
191
280
  }
192
281
  // 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 reference,\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 reference,\n onSuccess,\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\";\nimport { StablePayPayArgs } from \"../types\";\n\ntype PendingPayment = StablePayPayArgs;\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 }: StablePayPayArgs) => {\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, reference, onSuccess, onError }: StablePayPayArgs) => {\n setLoading(true);\n setSignature(null);\n setError(null);\n\n // Not connected → store intent + callbacks\n if (!connected || !publicKey) {\n setPendingPayment({ amount, to, reference, onSuccess, onError });\n setVisible(true);\n return;\n }\n\n try {\n const sig = await executePayment({ amount, to });\n onSuccess?.({\n signature: sig,\n amount,\n to,\n reference,\n });\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?.({\n signature: sig,\n amount: payment.amount,\n to: payment.to,\n reference: payment.reference,\n });\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,57 +104,130 @@ 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, reference, onSuccess, onError }) => {
95
141
  setLoading(true);
96
142
  setSignature(null);
97
143
  setError(null);
144
+ if (!connected || !publicKey) {
145
+ setPendingPayment({ amount, to, reference, 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
150
+ const sig = await executePayment({ amount, to });
151
+ onSuccess?.({
152
+ signature: sig,
153
+ amount,
154
+ to,
155
+ reference
112
156
  });
113
- const sig = await sendTransaction(transaction, connection);
114
- await connection.confirmTransaction(sig, "confirmed");
115
- setSignature(sig);
116
- return sig;
117
157
  } catch (err) {
118
158
  const normalized = err instanceof Error ? err : new Error("Payment failed");
119
159
  setError(normalized);
120
- throw normalized;
160
+ onError?.(normalized);
121
161
  } finally {
122
162
  setLoading(false);
123
163
  }
124
164
  },
125
- [connected, publicKey, sendTransaction, connection, connect, setVisible]
165
+ [connected, publicKey, executePayment, setVisible]
126
166
  );
167
+ useEffect(() => {
168
+ if (!connected || !publicKey || !pendingPayment) return;
169
+ const payment = pendingPayment;
170
+ let cancelled = false;
171
+ async function resumePayment() {
172
+ try {
173
+ const sig = await executePayment(payment);
174
+ if (!cancelled) {
175
+ payment.onSuccess?.({
176
+ signature: sig,
177
+ amount: payment.amount,
178
+ to: payment.to,
179
+ reference: payment.reference
180
+ });
181
+ setSignature(sig);
182
+ setPendingPayment(null);
183
+ }
184
+ } catch (err) {
185
+ if (!cancelled) {
186
+ const normalized = err instanceof Error ? err : new Error("Payment failed");
187
+ setError(normalized);
188
+ payment.onError?.(normalized);
189
+ setPendingPayment(null);
190
+ }
191
+ } finally {
192
+ if (!cancelled) {
193
+ setLoading(false);
194
+ }
195
+ }
196
+ }
197
+ resumePayment();
198
+ return () => {
199
+ cancelled = true;
200
+ };
201
+ }, [connected, publicKey, pendingPayment, executePayment]);
127
202
  return { pay, loading, error, signature };
128
203
  }
129
204
 
130
205
  // src/components/StablePay.tsx
206
+ function getResolvedTo(to, defaultTo) {
207
+ const resolved = to ?? defaultTo;
208
+ if (!resolved) {
209
+ throw new Error(
210
+ "StablePay: missing recipient address. Provide `to` on StablePay or StablePayProvider."
211
+ );
212
+ }
213
+ return resolved;
214
+ }
131
215
  function StablePay({
132
216
  amount,
133
217
  to,
218
+ reference,
134
219
  children,
135
220
  onSuccess,
136
221
  onError
137
222
  }) {
138
223
  const { pay, loading } = useStablePay();
224
+ const { defaultTo } = useStablePayContext();
225
+ const resolvedTo = getResolvedTo(to, defaultTo);
226
+ if (!resolvedTo) {
227
+ throw new Error(
228
+ "StablePay: missing recipient address. Provide `to` on StablePay or StablePayProvider."
229
+ );
230
+ }
139
231
  if (!React2.isValidElement(children)) {
140
232
  throw new Error("StablePay expects a single React element child.");
141
233
  }
@@ -143,17 +235,20 @@ function StablePay({
143
235
  try {
144
236
  children.props.onClick?.(event);
145
237
  if (event.defaultPrevented) return;
146
- const signature = await pay({ amount, to });
147
- onSuccess?.({ signature, amount, to });
238
+ await pay({
239
+ amount,
240
+ to: resolvedTo,
241
+ reference,
242
+ onSuccess,
243
+ onError
244
+ });
148
245
  } catch (err) {
149
246
  onError?.(err instanceof Error ? err : new Error("Payment failed"));
150
247
  }
151
248
  }
152
249
  return React2.cloneElement(children, {
153
250
  onClick: handleClick,
154
- disabled: Boolean(children.props.disabled) || loading,
155
- "aria-busy": loading,
156
- "data-stablepay-loading": loading ? "true" : "false"
251
+ disabled: Boolean(children.props.disabled) || loading
157
252
  });
158
253
  }
159
254
  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 reference,\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 reference,\n onSuccess,\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\";\nimport { StablePayPayArgs } from \"../types\";\n\ntype PendingPayment = StablePayPayArgs;\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 }: StablePayPayArgs) => {\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, reference, onSuccess, onError }: StablePayPayArgs) => {\n setLoading(true);\n setSignature(null);\n setError(null);\n\n // Not connected → store intent + callbacks\n if (!connected || !publicKey) {\n setPendingPayment({ amount, to, reference, onSuccess, onError });\n setVisible(true);\n return;\n }\n\n try {\n const sig = await executePayment({ amount, to });\n onSuccess?.({\n signature: sig,\n amount,\n to,\n reference,\n });\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?.({\n signature: sig,\n amount: payment.amount,\n to: payment.to,\n reference: payment.reference,\n });\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.3.0",
4
4
  "description": "Headless React wrapper for USDC payments on Solana",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",