@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 +21 -9
- package/dist/index.d.ts +21 -9
- package/dist/index.js +117 -38
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +117 -32
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
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
|
|
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
|
-
|
|
37
|
+
declare function StablePay({ amount, to, children, onSuccess, onError, }: StablePayProps): React.ReactElement<{
|
|
34
38
|
onClick?: (event: React.MouseEvent) => void;
|
|
35
39
|
disabled?: boolean;
|
|
36
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
37
|
+
declare function StablePay({ amount, to, children, onSuccess, onError, }: StablePayProps): React.ReactElement<{
|
|
34
38
|
onClick?: (event: React.MouseEvent) => void;
|
|
35
39
|
disabled?: boolean;
|
|
36
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
59
|
+
endpoint,
|
|
60
|
+
to
|
|
49
61
|
}) {
|
|
50
|
-
const
|
|
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
|
|
71
|
+
var import_react4 = __toESM(require("react"));
|
|
59
72
|
|
|
60
73
|
// src/hooks/useStablePay.ts
|
|
61
|
-
var
|
|
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
|
|
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,
|
|
124
|
-
const [signature, setSignature] = (0,
|
|
125
|
-
const [error, setError] = (0,
|
|
126
|
-
const
|
|
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
|
-
|
|
133
|
-
|
|
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
|
-
|
|
182
|
+
onError?.(normalized);
|
|
154
183
|
} finally {
|
|
155
184
|
setLoading(false);
|
|
156
185
|
}
|
|
157
186
|
},
|
|
158
|
-
[connected, publicKey,
|
|
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
|
-
|
|
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
|
-
|
|
180
|
-
|
|
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
|
|
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 {
|
|
3
|
+
import {
|
|
4
|
+
ConnectionProvider,
|
|
5
|
+
WalletProvider
|
|
6
|
+
} from "@solana/wallet-adapter-react";
|
|
4
7
|
import { WalletModalProvider } from "@solana/wallet-adapter-react-ui";
|
|
5
|
-
import {
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
100
|
-
|
|
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
|
-
|
|
155
|
+
onError?.(normalized);
|
|
121
156
|
} finally {
|
|
122
157
|
setLoading(false);
|
|
123
158
|
}
|
|
124
159
|
},
|
|
125
|
-
[connected, publicKey,
|
|
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
|
-
|
|
147
|
-
|
|
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 {
|
package/dist/index.mjs.map
CHANGED
|
@@ -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"]}
|