@coin-voyage/paykit 2.4.3-beta.0 → 2.4.4-beta.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.
@@ -3,23 +3,24 @@ import { PaymentRail, StepKind, } from "@coin-voyage/shared/types";
3
3
  import { assert } from "@coin-voyage/shared/utils";
4
4
  import { useBackendApi } from "../components/contexts/api";
5
5
  import { fetchPaymentDetails } from "../lib/api/payment-details";
6
+ import { isCryptoPaymentData, isDepositStepData } from "@coin-voyage/shared/payment";
6
7
  async function executeWalletStep({ actions, paymentData, senderAddr, step, }) {
7
8
  if (step.kind === StepKind.KIND_TRANSACTION) {
8
- assert(Boolean(step.data?.crypto), "Transaction step is missing executable wallet data");
9
+ assert(isCryptoPaymentData(step.data), "Transaction step is missing executable wallet data");
9
10
  return actions.execute({
10
11
  from: senderAddr,
11
- paymentData: step.data?.crypto,
12
+ paymentData: step.data,
12
13
  });
13
14
  }
14
15
  assert(step.kind === StepKind.KIND_DEPOSIT, "Unsupported wallet payment step");
15
- assert(Boolean(step.deposit_address), "Deposit step is missing a deposit address");
16
+ assert(isDepositStepData(step.data), "Deposit step is missing a deposit address");
16
17
  return actions.execute({
17
18
  amount: BigInt(paymentData.src.currency_amount.raw_amount),
18
19
  from: senderAddr,
19
- to: step.deposit_address,
20
+ to: step.data.deposit_address,
20
21
  chainId: paymentData.src.chain_id,
21
- token: paymentData.src.address
22
- ? { address: paymentData.src.address, decimals: paymentData.src.decimals }
22
+ token: step.data.currency.address
23
+ ? { address: step.data.currency.address, decimals: paymentData.src.decimals }
23
24
  : undefined,
24
25
  });
25
26
  }
@@ -4,13 +4,17 @@ type PaymentLifecycleHandlers = {
4
4
  onPaymentCompleted: ((event: PayOrderCompletedEvent) => void) | undefined;
5
5
  onPaymentBounced: ((event: PayOrderRefundedEvent) => void) | undefined;
6
6
  };
7
+ type PaymentLifecycleOptions = {
8
+ optimisticConfirmation?: boolean;
9
+ };
7
10
  /**
8
11
  * Handles payment lifecycle events of an order, such as started, completed, and bounced.
9
12
  * @param {PayOrder | undefined} order The pay order to monitor.
10
13
  * @param {PaymentLifecycleHandlers} handlers Handlers for payment lifecycle events.
14
+ * @param {PaymentLifecycleOptions} options Lifecycle behavior flags.
11
15
  * @returns
12
16
  */
13
- export declare function usePaymentLifecycle(order: PayOrder | undefined, handlers: PaymentLifecycleHandlers): {
17
+ export declare function usePaymentLifecycle(order: PayOrder | undefined, handlers: PaymentLifecycleHandlers, options?: PaymentLifecycleOptions): {
14
18
  isStarted: boolean;
15
19
  isFinalized: boolean;
16
20
  order: PayOrder | undefined;
@@ -1,76 +1,82 @@
1
1
  import { PayOrderMode, PayOrderStatus, } from "@coin-voyage/shared/types";
2
- import { useEffect, useMemo, useRef } from "react";
2
+ import { useEffect, useRef } from "react";
3
+ const COMPLETED_STATES = [PayOrderStatus.COMPLETED, PayOrderStatus.REFUNDED];
4
+ const STARTED_STATES = [
5
+ ...COMPLETED_STATES,
6
+ PayOrderStatus.PARTIAL_PAYMENT,
7
+ PayOrderStatus.AWAITING_CONFIRMATION,
8
+ PayOrderStatus.OPTIMISTIC_CONFIRMED,
9
+ PayOrderStatus.EXECUTING_ORDER,
10
+ ];
3
11
  /**
4
12
  * Handles payment lifecycle events of an order, such as started, completed, and bounced.
5
13
  * @param {PayOrder | undefined} order The pay order to monitor.
6
14
  * @param {PaymentLifecycleHandlers} handlers Handlers for payment lifecycle events.
15
+ * @param {PaymentLifecycleOptions} options Lifecycle behavior flags.
7
16
  * @returns
8
17
  */
9
- export function usePaymentLifecycle(order, handlers) {
18
+ export function usePaymentLifecycle(order, handlers, options = {}) {
10
19
  const sentStart = useRef(false);
11
20
  const sentComplete = useRef(false);
21
+ const currentOrderId = useRef(undefined);
12
22
  const { onPaymentStarted, onPaymentCompleted, onPaymentBounced } = handlers;
23
+ const { optimisticConfirmation = true } = options;
24
+ const orderId = order?.id;
13
25
  const orderStatus = order?.status;
14
- const isSale = order?.mode === PayOrderMode.SALE;
15
- const completedStates = useMemo(() => [PayOrderStatus.COMPLETED, PayOrderStatus.REFUNDED], []);
16
- const isStarted = useMemo(() => {
17
- if (!orderStatus)
18
- return false;
19
- const startedStatus = [
20
- ...completedStates,
21
- PayOrderStatus.PARTIAL_PAYMENT,
22
- PayOrderStatus.AWAITING_CONFIRMATION,
23
- PayOrderStatus.OPTIMISTIC_CONFIRMED,
24
- PayOrderStatus.EXECUTING_ORDER,
25
- ];
26
- return startedStatus.includes(orderStatus);
27
- }, [orderStatus, completedStates]);
28
- const isFinalized = useMemo(() => {
29
- if (!orderStatus)
30
- return false;
31
- const finalizedStatus = [...completedStates];
32
- if (isSale) {
33
- finalizedStatus.push(PayOrderStatus.OPTIMISTIC_CONFIRMED, PayOrderStatus.EXECUTING_ORDER);
34
- }
35
- return finalizedStatus.includes(orderStatus);
36
- }, [orderStatus, isSale, completedStates]);
26
+ const orderMetadata = order?.metadata;
27
+ const payment = order?.payment;
28
+ const allowOptimisticCompletion = optimisticConfirmation && order?.mode === PayOrderMode.SALE;
29
+ const isStarted = !!orderStatus && STARTED_STATES.includes(orderStatus);
30
+ const isFinalized = !!orderStatus &&
31
+ (COMPLETED_STATES.includes(orderStatus) ||
32
+ (allowOptimisticCompletion &&
33
+ (orderStatus === PayOrderStatus.OPTIMISTIC_CONFIRMED || orderStatus === PayOrderStatus.EXECUTING_ORDER)));
34
+ useEffect(() => {
35
+ if (!orderId)
36
+ return;
37
+ if (currentOrderId.current === orderId)
38
+ return;
39
+ currentOrderId.current = orderId;
40
+ sentStart.current = false;
41
+ sentComplete.current = false;
42
+ }, [orderId]);
37
43
  useEffect(() => {
38
- if (sentStart.current || !order?.payment || !isStarted)
44
+ if (sentStart.current || !orderId || !payment || !orderStatus || !isStarted)
39
45
  return;
40
46
  sentStart.current = true;
41
47
  onPaymentStarted?.({
42
48
  type: "payorder_confirming",
43
- payorder_id: order.id,
44
- status: order.status,
45
- metadata: order.metadata,
46
- payment_data: order.payment,
49
+ payorder_id: orderId,
50
+ status: orderStatus,
51
+ metadata: orderMetadata,
52
+ payment_data: payment,
47
53
  });
48
- }, [order, isStarted, onPaymentStarted]);
54
+ }, [isStarted, onPaymentStarted, orderId, orderMetadata, orderStatus, payment]);
49
55
  useEffect(() => {
50
- if (sentComplete.current || !order?.payment || !isFinalized)
56
+ if (sentComplete.current || !orderId || !payment || !orderStatus || !isFinalized)
51
57
  return;
52
58
  sentComplete.current = true;
53
- if (order.status === PayOrderStatus.REFUNDED) {
59
+ if (orderStatus === PayOrderStatus.REFUNDED) {
54
60
  onPaymentBounced?.({
55
61
  type: "payorder_refunded",
56
- payorder_id: order.id,
57
- status: order.status,
58
- metadata: order.metadata,
59
- payment_data: order.payment,
60
- refund_address: order.payment.refund_address ?? "",
61
- refund_tx_hash: order.payment.refund_tx_hash ?? "",
62
+ payorder_id: orderId,
63
+ status: orderStatus,
64
+ metadata: orderMetadata,
65
+ payment_data: payment,
66
+ refund_address: payment.refund_address ?? "",
67
+ refund_tx_hash: payment.refund_tx_hash ?? "",
62
68
  });
63
69
  return;
64
70
  }
65
71
  onPaymentCompleted?.({
66
72
  type: "payorder_completed",
67
- payorder_id: order.id,
68
- status: order.status,
69
- metadata: order.metadata,
70
- payment_data: order.payment,
71
- source_tx_hash: order.payment.source_tx_hash ?? "",
72
- destination_tx_hash: order.payment.destination_tx_hash ?? "",
73
+ payorder_id: orderId,
74
+ status: orderStatus,
75
+ metadata: orderMetadata,
76
+ payment_data: payment,
77
+ source_tx_hash: payment.source_tx_hash ?? "",
78
+ destination_tx_hash: payment.destination_tx_hash ?? "",
73
79
  });
74
- }, [order, isFinalized, onPaymentCompleted, onPaymentBounced]);
80
+ }, [isFinalized, onPaymentBounced, onPaymentCompleted, orderId, orderMetadata, orderStatus, payment]);
75
81
  return { isStarted, isFinalized, order };
76
82
  }
@@ -5,10 +5,11 @@ import { CurrencyAndQuoteID } from "../types/state";
5
5
  import { usePayOrderQuotes } from "./usePayOrderQuotes";
6
6
  /** Loads a PayOrder + manages the corresponding modal. */
7
7
  export interface PaymentState {
8
- setPayId: (id: string | null) => Promise<void>;
9
- createDepositPayOrder: (params: PayOrderParams, onError?: (message: string) => void) => Promise<void>;
8
+ setPayId: (id: string | null) => Promise<PayOrder | undefined>;
9
+ createDepositPayOrder: (params: PayOrderParams, onError?: (message: string) => void) => Promise<PayOrder | undefined>;
10
10
  copyDepositPayOrder: () => Promise<void>;
11
11
  clearUserSelection: () => void;
12
+ resetPaymentState: () => void;
12
13
  payOrder: PayOrder | undefined;
13
14
  paymentMethod: PaymentMethod | undefined;
14
15
  setPaymentMethod: React.Dispatch<React.SetStateAction<PaymentMethod | undefined>>;
@@ -31,7 +32,7 @@ export interface PaymentState {
31
32
  }
32
33
  export declare function usePaymentState({ payOrder, setPayOrder, setRoute, log, }: {
33
34
  payOrder: PayOrder | undefined;
34
- setPayOrder: (o: PayOrder) => void;
35
+ setPayOrder: (o: PayOrder | undefined) => void;
35
36
  setRoute: React.Dispatch<React.SetStateAction<ROUTE>>;
36
37
  log: (...args: unknown[]) => void;
37
38
  }): PaymentState;
@@ -15,7 +15,8 @@ export function usePaymentState({ payOrder, setPayOrder, setRoute, log, }) {
15
15
  const api = useBackendApi();
16
16
  const [connectorChainType, setConnectorChainType] = useState();
17
17
  const [selectedWallet, setSelectedWallet] = useState();
18
- const lastRequestedIdRef = useRef(null);
18
+ const latestRequestedIdRef = useRef(null);
19
+ const inFlightPayOrderIdsRef = useRef(new Set());
19
20
  const { account: senderAccount } = useAccount({
20
21
  selectedWallet,
21
22
  chainType: connectorChainType,
@@ -74,13 +75,23 @@ export function usePaymentState({ payOrder, setPayOrder, setRoute, log, }) {
74
75
  const setPayOrderId = useCallback(async (payOrderId) => {
75
76
  if (!payOrderId)
76
77
  return;
77
- if (lastRequestedIdRef.current === payOrderId)
78
+ if (payOrder?.id === payOrderId)
78
79
  return;
79
- lastRequestedIdRef.current = payOrderId;
80
- const order = await fetchPayOrder(payOrderId);
81
- if (order)
82
- setPayOrder(order);
83
- }, [fetchPayOrder, setPayOrder]);
80
+ if (inFlightPayOrderIdsRef.current.has(payOrderId))
81
+ return;
82
+ latestRequestedIdRef.current = payOrderId;
83
+ inFlightPayOrderIdsRef.current.add(payOrderId);
84
+ try {
85
+ const order = await fetchPayOrder(payOrderId);
86
+ if (order && latestRequestedIdRef.current === payOrderId) {
87
+ setPayOrder(order);
88
+ }
89
+ return order;
90
+ }
91
+ finally {
92
+ inFlightPayOrderIdsRef.current.delete(payOrderId);
93
+ }
94
+ }, [fetchPayOrder, payOrder?.id, setPayOrder]);
84
95
  const copyDepositPayOrder = useCallback(async () => {
85
96
  if (!payOrder?.id) {
86
97
  log(`No payOrder to copy`);
@@ -124,10 +135,13 @@ export function usePaymentState({ payOrder, setPayOrder, setRoute, log, }) {
124
135
  }
125
136
  const { data: payOrder, error } = await api.createDepositPayOrder(params);
126
137
  if (!payOrder || error) {
138
+ const errorMessage = error?.message || "Unable to create payment order";
139
+ onError?.(errorMessage);
127
140
  log(`[CREATE DEPOSIT] Error creating payOrder: ${JSON.stringify(error)}`);
128
141
  return;
129
142
  }
130
143
  setPayOrder(payOrder);
144
+ return payOrder;
131
145
  }
132
146
  catch (e) {
133
147
  if (e instanceof Error)
@@ -142,11 +156,24 @@ export function usePaymentState({ payOrder, setPayOrder, setRoute, log, }) {
142
156
  setSelectedWallet(undefined);
143
157
  setRoute(ROUTE.SELECT_METHOD);
144
158
  }, [setRoute]);
159
+ const resetPaymentState = useCallback(() => {
160
+ latestRequestedIdRef.current = null;
161
+ inFlightPayOrderIdsRef.current.clear();
162
+ setPayOrder(undefined);
163
+ setPaymentMethod(undefined);
164
+ setSelectedCurrencyOption(undefined);
165
+ setConnectorChainType(undefined);
166
+ setSelectedWallet(undefined);
167
+ setPayToAddressChainId(undefined);
168
+ setPayToAddressCurrency(undefined);
169
+ setRoute(ROUTE.SELECT_METHOD);
170
+ }, [setPayOrder, setRoute]);
145
171
  return {
146
172
  setPayId: setPayOrderId,
147
173
  createDepositPayOrder,
148
174
  copyDepositPayOrder,
149
175
  clearUserSelection,
176
+ resetPaymentState,
150
177
  payOrder,
151
178
  paymentMethod,
152
179
  setPaymentMethod,
@@ -0,0 +1,182 @@
1
+ import type { ChainId } from "@coin-voyage/shared/types";
2
+ export declare function useTokenList(chainId?: ChainId): {
3
+ tokenList: import("@coin-voyage/shared/currency").TokenListResponse | undefined;
4
+ chains: import("@coin-voyage/shared/currency").ChainMetadata[];
5
+ tokens: import("@coin-voyage/shared/currency").Token[];
6
+ data: import("@coin-voyage/shared/currency").TokenListResponse;
7
+ error: Error;
8
+ isError: true;
9
+ isPending: false;
10
+ isLoading: false;
11
+ isLoadingError: false;
12
+ isRefetchError: true;
13
+ isSuccess: false;
14
+ isPlaceholderData: false;
15
+ status: "error";
16
+ dataUpdatedAt: number;
17
+ errorUpdatedAt: number;
18
+ failureCount: number;
19
+ failureReason: Error | null;
20
+ errorUpdateCount: number;
21
+ isFetched: boolean;
22
+ isFetchedAfterMount: boolean;
23
+ isFetching: boolean;
24
+ isInitialLoading: boolean;
25
+ isPaused: boolean;
26
+ isRefetching: boolean;
27
+ isStale: boolean;
28
+ isEnabled: boolean;
29
+ refetch: (options?: import("@tanstack/react-query").RefetchOptions) => Promise<import("@tanstack/react-query").QueryObserverResult<import("@coin-voyage/shared/currency").TokenListResponse, Error>>;
30
+ fetchStatus: import("@tanstack/react-query").FetchStatus;
31
+ promise: Promise<import("@coin-voyage/shared/currency").TokenListResponse>;
32
+ } | {
33
+ tokenList: import("@coin-voyage/shared/currency").TokenListResponse | undefined;
34
+ chains: import("@coin-voyage/shared/currency").ChainMetadata[];
35
+ tokens: import("@coin-voyage/shared/currency").Token[];
36
+ data: import("@coin-voyage/shared/currency").TokenListResponse;
37
+ error: null;
38
+ isError: false;
39
+ isPending: false;
40
+ isLoading: false;
41
+ isLoadingError: false;
42
+ isRefetchError: false;
43
+ isSuccess: true;
44
+ isPlaceholderData: false;
45
+ status: "success";
46
+ dataUpdatedAt: number;
47
+ errorUpdatedAt: number;
48
+ failureCount: number;
49
+ failureReason: Error | null;
50
+ errorUpdateCount: number;
51
+ isFetched: boolean;
52
+ isFetchedAfterMount: boolean;
53
+ isFetching: boolean;
54
+ isInitialLoading: boolean;
55
+ isPaused: boolean;
56
+ isRefetching: boolean;
57
+ isStale: boolean;
58
+ isEnabled: boolean;
59
+ refetch: (options?: import("@tanstack/react-query").RefetchOptions) => Promise<import("@tanstack/react-query").QueryObserverResult<import("@coin-voyage/shared/currency").TokenListResponse, Error>>;
60
+ fetchStatus: import("@tanstack/react-query").FetchStatus;
61
+ promise: Promise<import("@coin-voyage/shared/currency").TokenListResponse>;
62
+ } | {
63
+ tokenList: import("@coin-voyage/shared/currency").TokenListResponse | undefined;
64
+ chains: import("@coin-voyage/shared/currency").ChainMetadata[];
65
+ tokens: import("@coin-voyage/shared/currency").Token[];
66
+ data: undefined;
67
+ error: Error;
68
+ isError: true;
69
+ isPending: false;
70
+ isLoading: false;
71
+ isLoadingError: true;
72
+ isRefetchError: false;
73
+ isSuccess: false;
74
+ isPlaceholderData: false;
75
+ status: "error";
76
+ dataUpdatedAt: number;
77
+ errorUpdatedAt: number;
78
+ failureCount: number;
79
+ failureReason: Error | null;
80
+ errorUpdateCount: number;
81
+ isFetched: boolean;
82
+ isFetchedAfterMount: boolean;
83
+ isFetching: boolean;
84
+ isInitialLoading: boolean;
85
+ isPaused: boolean;
86
+ isRefetching: boolean;
87
+ isStale: boolean;
88
+ isEnabled: boolean;
89
+ refetch: (options?: import("@tanstack/react-query").RefetchOptions) => Promise<import("@tanstack/react-query").QueryObserverResult<import("@coin-voyage/shared/currency").TokenListResponse, Error>>;
90
+ fetchStatus: import("@tanstack/react-query").FetchStatus;
91
+ promise: Promise<import("@coin-voyage/shared/currency").TokenListResponse>;
92
+ } | {
93
+ tokenList: import("@coin-voyage/shared/currency").TokenListResponse | undefined;
94
+ chains: import("@coin-voyage/shared/currency").ChainMetadata[];
95
+ tokens: import("@coin-voyage/shared/currency").Token[];
96
+ data: undefined;
97
+ error: null;
98
+ isError: false;
99
+ isPending: true;
100
+ isLoading: true;
101
+ isLoadingError: false;
102
+ isRefetchError: false;
103
+ isSuccess: false;
104
+ isPlaceholderData: false;
105
+ status: "pending";
106
+ dataUpdatedAt: number;
107
+ errorUpdatedAt: number;
108
+ failureCount: number;
109
+ failureReason: Error | null;
110
+ errorUpdateCount: number;
111
+ isFetched: boolean;
112
+ isFetchedAfterMount: boolean;
113
+ isFetching: boolean;
114
+ isInitialLoading: boolean;
115
+ isPaused: boolean;
116
+ isRefetching: boolean;
117
+ isStale: boolean;
118
+ isEnabled: boolean;
119
+ refetch: (options?: import("@tanstack/react-query").RefetchOptions) => Promise<import("@tanstack/react-query").QueryObserverResult<import("@coin-voyage/shared/currency").TokenListResponse, Error>>;
120
+ fetchStatus: import("@tanstack/react-query").FetchStatus;
121
+ promise: Promise<import("@coin-voyage/shared/currency").TokenListResponse>;
122
+ } | {
123
+ tokenList: import("@coin-voyage/shared/currency").TokenListResponse | undefined;
124
+ chains: import("@coin-voyage/shared/currency").ChainMetadata[];
125
+ tokens: import("@coin-voyage/shared/currency").Token[];
126
+ data: undefined;
127
+ error: null;
128
+ isError: false;
129
+ isPending: true;
130
+ isLoadingError: false;
131
+ isRefetchError: false;
132
+ isSuccess: false;
133
+ isPlaceholderData: false;
134
+ status: "pending";
135
+ dataUpdatedAt: number;
136
+ errorUpdatedAt: number;
137
+ failureCount: number;
138
+ failureReason: Error | null;
139
+ errorUpdateCount: number;
140
+ isFetched: boolean;
141
+ isFetchedAfterMount: boolean;
142
+ isFetching: boolean;
143
+ isLoading: boolean;
144
+ isInitialLoading: boolean;
145
+ isPaused: boolean;
146
+ isRefetching: boolean;
147
+ isStale: boolean;
148
+ isEnabled: boolean;
149
+ refetch: (options?: import("@tanstack/react-query").RefetchOptions) => Promise<import("@tanstack/react-query").QueryObserverResult<import("@coin-voyage/shared/currency").TokenListResponse, Error>>;
150
+ fetchStatus: import("@tanstack/react-query").FetchStatus;
151
+ promise: Promise<import("@coin-voyage/shared/currency").TokenListResponse>;
152
+ } | {
153
+ tokenList: import("@coin-voyage/shared/currency").TokenListResponse | undefined;
154
+ chains: import("@coin-voyage/shared/currency").ChainMetadata[];
155
+ tokens: import("@coin-voyage/shared/currency").Token[];
156
+ data: import("@coin-voyage/shared/currency").TokenListResponse;
157
+ isError: false;
158
+ error: null;
159
+ isPending: false;
160
+ isLoading: false;
161
+ isLoadingError: false;
162
+ isRefetchError: false;
163
+ isSuccess: true;
164
+ isPlaceholderData: true;
165
+ status: "success";
166
+ dataUpdatedAt: number;
167
+ errorUpdatedAt: number;
168
+ failureCount: number;
169
+ failureReason: Error | null;
170
+ errorUpdateCount: number;
171
+ isFetched: boolean;
172
+ isFetchedAfterMount: boolean;
173
+ isFetching: boolean;
174
+ isInitialLoading: boolean;
175
+ isPaused: boolean;
176
+ isRefetching: boolean;
177
+ isStale: boolean;
178
+ isEnabled: boolean;
179
+ refetch: (options?: import("@tanstack/react-query").RefetchOptions) => Promise<import("@tanstack/react-query").QueryObserverResult<import("@coin-voyage/shared/currency").TokenListResponse, Error>>;
180
+ fetchStatus: import("@tanstack/react-query").FetchStatus;
181
+ promise: Promise<import("@coin-voyage/shared/currency").TokenListResponse>;
182
+ };
@@ -0,0 +1,24 @@
1
+ import { fetchTokenList, getChains, tokensByChainId } from "@coin-voyage/shared/currency";
2
+ import { useQuery } from "@tanstack/react-query";
3
+ import { useMemo } from "react";
4
+ const TOKEN_LIST_QUERY_KEY = ["token-list"];
5
+ const TOKEN_LIST_STALE_TIME = 1000 * 60 * 15;
6
+ export function useTokenList(chainId) {
7
+ const query = useQuery({
8
+ queryKey: TOKEN_LIST_QUERY_KEY,
9
+ queryFn: () => fetchTokenList(),
10
+ staleTime: TOKEN_LIST_STALE_TIME,
11
+ });
12
+ const chains = useMemo(() => getChains(query.data?.chains ?? []), [query.data]);
13
+ const tokens = useMemo(() => {
14
+ if (!query.data || !chainId)
15
+ return [];
16
+ return tokensByChainId(query.data.chains, chainId);
17
+ }, [query.data, chainId]);
18
+ return {
19
+ ...query,
20
+ tokenList: query.data,
21
+ chains,
22
+ tokens,
23
+ };
24
+ }
@@ -33,7 +33,7 @@ export function useTokenOptions(disabled) {
33
33
  disabled: isDisabled,
34
34
  iconShape: "squircle",
35
35
  icons: [
36
- _jsx(TokenChainLogo, { src: quote.image_uri, alt: quote.ticker, chainId: quote.chain_id }, quote.ticker + quote.chain_id),
36
+ _jsx(TokenChainLogo, { src: quote.image_uri, alt: `${quote.ticker} logo`, chainId: quote.chain_id }, `${quote.ticker}-${quote.chain_id}`),
37
37
  ],
38
38
  onClick: () => {
39
39
  setSelectedCurrencyOption(quote);
@@ -62,6 +62,10 @@ function PayKitProviderInternal({ theme = "auto", mode = "auto", customTheme, op
62
62
  const [resize, onResize] = useState(0);
63
63
  const onOpenRef = useRef(undefined);
64
64
  const onCloseRef = useRef(undefined);
65
+ const openRef = useRef(false);
66
+ useEffect(() => {
67
+ openRef.current = open;
68
+ }, [open]);
65
69
  const setOnOpen = useCallback((fn) => {
66
70
  onOpenRef.current = fn;
67
71
  }, []);
@@ -101,20 +105,23 @@ function PayKitProviderInternal({ theme = "auto", mode = "auto", customTheme, op
101
105
  setErrorMessage(null);
102
106
  }, [route, open]);
103
107
  const resetAfterClose = useCallback(() => {
104
- if (paymentCompleted && modalOptions?.resetOnSuccess) {
105
- setPaymentCompleted(false);
106
- paymentStateRef.current.clearUserSelection();
107
- }
108
+ if (!paymentCompleted || !modalOptions?.resetOnSuccess)
109
+ return;
110
+ setPaymentCompleted(false);
111
+ paymentStateRef.current.resetPaymentState();
108
112
  }, [modalOptions?.resetOnSuccess, paymentCompleted]);
109
113
  const setOpen = useCallback((nextOpen) => {
114
+ if (openRef.current === nextOpen) {
115
+ return;
116
+ }
117
+ openRef.current = nextOpen;
110
118
  setOpenState(nextOpen);
111
119
  if (nextOpen) {
112
120
  onOpenRef.current?.();
113
121
  return;
114
122
  }
115
- resetAfterClose();
116
123
  onCloseRef.current?.();
117
- }, [resetAfterClose]);
124
+ }, []);
118
125
  const onSuccess = useCallback(() => {
119
126
  setPaymentCompleted(true);
120
127
  if (modalOptions?.closeOnSuccess) {
@@ -247,7 +254,7 @@ function PayKitProviderInternal({ theme = "auto", mode = "auto", customTheme, op
247
254
  paymentState,
248
255
  allowedWallets,
249
256
  };
250
- return (_jsx(PayContext.Provider, { value: value, children: _jsxs(StyledThemeProvider, { theme: defaultTheme, children: [children, _jsx(PayModal, {})] }) }));
257
+ return (_jsx(PayContext.Provider, { value: value, children: _jsxs(StyledThemeProvider, { theme: defaultTheme, children: [children, _jsx(PayModal, { onExited: resetAfterClose })] }) }));
251
258
  }
252
259
  // === Helper functions ===
253
260
  const NON_FINAL_PAY_ORDER_STATUSES = [PayOrderStatus.PENDING, PayOrderStatus.AWAITING_PAYMENT, PayOrderStatus.EXPIRED];
@@ -1,17 +1,18 @@
1
1
  export declare enum ROUTE {
2
2
  SELECT_METHOD = 0,
3
- ADDRESS_CHAIN_SELECT = 1,
4
- ADDRESS_TOKEN_SELECT = 2,
5
- PAY_TO_ADDRESS = 3,
6
- WALLET_CHAIN_SELECT = 4,
7
- WALLET_TOKEN_SELECT = 5,
8
- CONNECTORS = 6,
9
- MOBILECONNECTORS = 7,
10
- CONNECT = 8,
11
- WALLET_PAYMENT = 9,
12
- CARD_PAYMENT = 10,
13
- CONFIRMATION = 11,
14
- ONBOARDING = 12,
15
- ABOUT = 13,
16
- DOWNLOAD = 14
3
+ PREPARING_PAYMENT = 1,
4
+ ADDRESS_CHAIN_SELECT = 2,
5
+ ADDRESS_TOKEN_SELECT = 3,
6
+ PAY_TO_ADDRESS = 4,
7
+ WALLET_CHAIN_SELECT = 5,
8
+ WALLET_TOKEN_SELECT = 6,
9
+ CONNECTORS = 7,
10
+ MOBILECONNECTORS = 8,
11
+ CONNECT = 9,
12
+ WALLET_PAYMENT = 10,
13
+ CARD_PAYMENT = 11,
14
+ CONFIRMATION = 12,
15
+ ONBOARDING = 13,
16
+ ABOUT = 14,
17
+ DOWNLOAD = 15
17
18
  }
@@ -1,19 +1,20 @@
1
1
  export var ROUTE;
2
2
  (function (ROUTE) {
3
3
  ROUTE[ROUTE["SELECT_METHOD"] = 0] = "SELECT_METHOD";
4
- ROUTE[ROUTE["ADDRESS_CHAIN_SELECT"] = 1] = "ADDRESS_CHAIN_SELECT";
5
- ROUTE[ROUTE["ADDRESS_TOKEN_SELECT"] = 2] = "ADDRESS_TOKEN_SELECT";
6
- ROUTE[ROUTE["PAY_TO_ADDRESS"] = 3] = "PAY_TO_ADDRESS";
7
- ROUTE[ROUTE["WALLET_CHAIN_SELECT"] = 4] = "WALLET_CHAIN_SELECT";
8
- ROUTE[ROUTE["WALLET_TOKEN_SELECT"] = 5] = "WALLET_TOKEN_SELECT";
9
- ROUTE[ROUTE["CONNECTORS"] = 6] = "CONNECTORS";
10
- ROUTE[ROUTE["MOBILECONNECTORS"] = 7] = "MOBILECONNECTORS";
11
- ROUTE[ROUTE["CONNECT"] = 8] = "CONNECT";
12
- ROUTE[ROUTE["WALLET_PAYMENT"] = 9] = "WALLET_PAYMENT";
13
- ROUTE[ROUTE["CARD_PAYMENT"] = 10] = "CARD_PAYMENT";
14
- ROUTE[ROUTE["CONFIRMATION"] = 11] = "CONFIRMATION";
4
+ ROUTE[ROUTE["PREPARING_PAYMENT"] = 1] = "PREPARING_PAYMENT";
5
+ ROUTE[ROUTE["ADDRESS_CHAIN_SELECT"] = 2] = "ADDRESS_CHAIN_SELECT";
6
+ ROUTE[ROUTE["ADDRESS_TOKEN_SELECT"] = 3] = "ADDRESS_TOKEN_SELECT";
7
+ ROUTE[ROUTE["PAY_TO_ADDRESS"] = 4] = "PAY_TO_ADDRESS";
8
+ ROUTE[ROUTE["WALLET_CHAIN_SELECT"] = 5] = "WALLET_CHAIN_SELECT";
9
+ ROUTE[ROUTE["WALLET_TOKEN_SELECT"] = 6] = "WALLET_TOKEN_SELECT";
10
+ ROUTE[ROUTE["CONNECTORS"] = 7] = "CONNECTORS";
11
+ ROUTE[ROUTE["MOBILECONNECTORS"] = 8] = "MOBILECONNECTORS";
12
+ ROUTE[ROUTE["CONNECT"] = 9] = "CONNECT";
13
+ ROUTE[ROUTE["WALLET_PAYMENT"] = 10] = "WALLET_PAYMENT";
14
+ ROUTE[ROUTE["CARD_PAYMENT"] = 11] = "CARD_PAYMENT";
15
+ ROUTE[ROUTE["CONFIRMATION"] = 12] = "CONFIRMATION";
15
16
  // Other
16
- ROUTE[ROUTE["ONBOARDING"] = 12] = "ONBOARDING";
17
- ROUTE[ROUTE["ABOUT"] = 13] = "ABOUT";
18
- ROUTE[ROUTE["DOWNLOAD"] = 14] = "DOWNLOAD";
17
+ ROUTE[ROUTE["ONBOARDING"] = 13] = "ONBOARDING";
18
+ ROUTE[ROUTE["ABOUT"] = 14] = "ABOUT";
19
+ ROUTE[ROUTE["DOWNLOAD"] = 15] = "DOWNLOAD";
19
20
  })(ROUTE || (ROUTE = {}));
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@coin-voyage/paykit",
3
3
  "description": "Seamless crypto payments. Onboard users from any chain, any coin into your app with one click.",
4
- "version": "2.4.3-beta.0",
4
+ "version": "2.4.4-beta.0",
5
5
  "private": false,
6
6
  "sideEffects": false,
7
7
  "author": "Lars <lars@coinvoyage.io>",
@@ -31,9 +31,6 @@
31
31
  ],
32
32
  "server": [
33
33
  "dist/server.d.ts"
34
- ],
35
- "client": [
36
- "dist/client.d.ts"
37
34
  ]
38
35
  }
39
36
  },
@@ -63,8 +60,8 @@
63
60
  "@stripe/crypto": "0.0.4",
64
61
  "styled-components": "^5.3.11",
65
62
  "uuid": "13.0.0",
66
- "@coin-voyage/shared": "2.4.3-beta.0",
67
- "@coin-voyage/crypto": "2.4.3-beta.0"
63
+ "@coin-voyage/shared": "2.4.4-beta.1",
64
+ "@coin-voyage/crypto": "2.4.3"
68
65
  },
69
66
  "devDependencies": {
70
67
  "@types/qrcode": "1.5.5",
@@ -1,5 +0,0 @@
1
- export interface AllowDeny<T> {
2
- allow?: T[];
3
- deny?: T[];
4
- }
5
- export declare const isItemAllowed: <T>(itemId: T, items?: AllowDeny<T>) => boolean;