@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 +20 -9
- package/dist/index.d.ts +20 -9
- package/dist/index.js +126 -37
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +126 -31
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
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
|
|
20
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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():
|
|
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
|
|
20
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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():
|
|
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
|
|
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,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
|
|
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, 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
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
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
|
-
|
|
187
|
+
onError?.(normalized);
|
|
154
188
|
} finally {
|
|
155
189
|
setLoading(false);
|
|
156
190
|
}
|
|
157
191
|
},
|
|
158
|
-
[connected, publicKey,
|
|
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
|
-
|
|
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
|
-
|
|
180
|
-
|
|
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
|
|
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 {
|
|
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,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
|
|
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, 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
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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
|
-
|
|
160
|
+
onError?.(normalized);
|
|
121
161
|
} finally {
|
|
122
162
|
setLoading(false);
|
|
123
163
|
}
|
|
124
164
|
},
|
|
125
|
-
[connected, publicKey,
|
|
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
|
-
|
|
147
|
-
|
|
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 {
|
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 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"]}
|