@daimo/pay 1.1.4 → 1.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.
Files changed (82) hide show
  1. package/build/index.d.ts +37 -105
  2. package/build/package.json.js +3 -4
  3. package/build/package.json.js.map +1 -1
  4. package/build/src/assets/icons.js +2 -1
  5. package/build/src/assets/icons.js.map +1 -1
  6. package/build/src/components/Common/AmountInput/AmountInputField.js +59 -0
  7. package/build/src/components/Common/AmountInput/AmountInputField.js.map +1 -0
  8. package/build/src/components/Common/AmountInput/index.js +143 -0
  9. package/build/src/components/Common/AmountInput/index.js.map +1 -0
  10. package/build/src/components/Common/ChainSelectList/index.js +1 -1
  11. package/build/src/components/Common/Modal/index.js +9 -4
  12. package/build/src/components/Common/Modal/index.js.map +1 -1
  13. package/build/src/components/Common/Modal/styles.js +2 -1
  14. package/build/src/components/Common/Modal/styles.js.map +1 -1
  15. package/build/src/components/Common/OptionsList/styles.js +1 -1
  16. package/build/src/components/Common/OrderHeader/index.js +22 -111
  17. package/build/src/components/Common/OrderHeader/index.js.map +1 -1
  18. package/build/src/components/Common/PaymentBreakdown/index.js +48 -0
  19. package/build/src/components/{Pages/WaitingOther → Common/PaymentBreakdown}/index.js.map +1 -1
  20. package/build/src/components/Common/SwitchButton/index.js +39 -0
  21. package/build/src/components/Common/SwitchButton/index.js.map +1 -0
  22. package/build/src/components/Common/TokenChainLogo/index.js +24 -0
  23. package/build/src/components/Common/TokenChainLogo/index.js.map +1 -0
  24. package/build/src/components/DaimoPay.js +7 -2
  25. package/build/src/components/DaimoPay.js.map +1 -1
  26. package/build/src/components/DaimoPayButton/index.js +0 -1
  27. package/build/src/components/DaimoPayButton/index.js.map +1 -1
  28. package/build/src/components/DaimoPayModal/index.js +86 -26
  29. package/build/src/components/DaimoPayModal/index.js.map +1 -1
  30. package/build/src/components/Pages/PayWithToken/index.js +4 -109
  31. package/build/src/components/Pages/PayWithToken/index.js.map +1 -1
  32. package/build/src/components/Pages/SelectAmount/index.js +16 -0
  33. package/build/src/components/Pages/SelectAmount/index.js.map +1 -0
  34. package/build/src/components/Pages/SelectDepositAddressAmount/index.js +60 -0
  35. package/build/src/components/Pages/SelectDepositAddressAmount/index.js.map +1 -0
  36. package/build/src/components/Pages/SelectDepositAddressChain/index.js +7 -2
  37. package/build/src/components/Pages/SelectDepositAddressChain/index.js.map +1 -1
  38. package/build/src/components/Pages/SelectExternalAmount/index.js +62 -0
  39. package/build/src/components/Pages/SelectExternalAmount/index.js.map +1 -0
  40. package/build/src/components/Pages/SelectMethod/index.js +14 -7
  41. package/build/src/components/Pages/SelectMethod/index.js.map +1 -1
  42. package/build/src/components/Pages/SelectToken/index.js +36 -41
  43. package/build/src/components/Pages/SelectToken/index.js.map +1 -1
  44. package/build/src/components/Pages/Solana/ConnectorSolana/index.js +8 -2
  45. package/build/src/components/Pages/Solana/ConnectorSolana/index.js.map +1 -1
  46. package/build/src/components/Pages/Solana/PayWithSolanaToken/index.js +14 -8
  47. package/build/src/components/Pages/Solana/PayWithSolanaToken/index.js.map +1 -1
  48. package/build/src/components/Pages/Solana/SelectSolanaAmount/index.js +16 -0
  49. package/build/src/components/Pages/Solana/SelectSolanaAmount/index.js.map +1 -0
  50. package/build/src/components/Pages/Solana/SelectSolanaToken/index.js +37 -21
  51. package/build/src/components/Pages/Solana/SelectSolanaToken/index.js.map +1 -1
  52. package/build/src/components/Pages/WaitingExternal/index.js +53 -0
  53. package/build/src/components/Pages/WaitingExternal/index.js.map +1 -0
  54. package/build/src/components/Spinners/ExternalPaymentSpinner/index.js +20 -0
  55. package/build/src/components/Spinners/ExternalPaymentSpinner/index.js.map +1 -0
  56. package/build/src/components/Spinners/TokenLogoSpinner/index.js +13 -0
  57. package/build/src/components/Spinners/TokenLogoSpinner/index.js.map +1 -0
  58. package/build/src/components/Spinners/TokenLogoSpinner/styles.js +40 -0
  59. package/build/src/components/Spinners/TokenLogoSpinner/styles.js.map +1 -0
  60. package/build/src/components/Spinners/styles.js +32 -0
  61. package/build/src/components/Spinners/styles.js.map +1 -0
  62. package/build/src/hooks/useDepositAddressOptions.js +8 -5
  63. package/build/src/hooks/useDepositAddressOptions.js.map +1 -1
  64. package/build/src/hooks/useExternalPaymentOptions.js +7 -6
  65. package/build/src/hooks/useExternalPaymentOptions.js.map +1 -1
  66. package/build/src/hooks/useOrderUsdLimits.js +26 -0
  67. package/build/src/hooks/useOrderUsdLimits.js.map +1 -0
  68. package/build/src/hooks/usePaymentState.js +46 -14
  69. package/build/src/hooks/usePaymentState.js.map +1 -1
  70. package/build/src/hooks/useSolanaPaymentOptions.js +7 -5
  71. package/build/src/hooks/useSolanaPaymentOptions.js.map +1 -1
  72. package/build/src/hooks/useWalletPaymentOptions.js +9 -8
  73. package/build/src/hooks/useWalletPaymentOptions.js.map +1 -1
  74. package/build/src/index.js +1 -1
  75. package/build/src/utils/exports.js +4 -1
  76. package/build/src/utils/exports.js.map +1 -1
  77. package/build/src/utils/format.js +83 -0
  78. package/build/src/utils/format.js.map +1 -0
  79. package/build/src/utils/validateInput.js +36 -0
  80. package/build/src/utils/validateInput.js.map +1 -0
  81. package/package.json +3 -4
  82. package/build/src/components/Pages/WaitingOther/index.js +0 -91
@@ -0,0 +1,40 @@
1
+ import { motion } from 'framer-motion';
2
+ import styled from 'styled-components';
3
+
4
+ const ChainLogoContainer = styled(motion.div) `
5
+ z-index: 10;
6
+ position: absolute;
7
+ right: 2px;
8
+ bottom: 2px;
9
+ padding: 0;
10
+ display: flex;
11
+ align-items: center;
12
+ justify-content: center;
13
+ width: 32px;
14
+ height: 32px;
15
+ border-radius: 16px;
16
+ overflow: hidden;
17
+
18
+ color: var(--ck-body-background);
19
+ transition: color 200ms ease;
20
+
21
+ &:before {
22
+ z-index: 5;
23
+ content: "";
24
+ position: absolute;
25
+ inset: 0;
26
+ opacity: 0;
27
+ transition: opacity 200ms ease;
28
+ background: var(--ck-body-color);
29
+ }
30
+
31
+ svg {
32
+ display: block;
33
+ position: relative;
34
+ width: 100%;
35
+ height: 100%;
36
+ }
37
+ `;
38
+
39
+ export { ChainLogoContainer };
40
+ //# sourceMappingURL=styles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"styles.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -0,0 +1,32 @@
1
+ import { motion } from 'framer-motion';
2
+ import styled, { css } from 'styled-components';
3
+
4
+ const LoadingContainer = styled(motion.div) `
5
+ display: flex;
6
+ align-items: center;
7
+ justify-content: center;
8
+ margin: 10px auto 16px;
9
+ height: 120px;
10
+ `;
11
+ const AnimationContainer = styled(motion.div) `
12
+ user-select: none;
13
+ position: relative;
14
+ --spinner-error-opacity: 0;
15
+ &:before {
16
+ content: "";
17
+ position: absolute;
18
+ inset: 1px;
19
+ opacity: 0;
20
+ background: var(--ck-body-color-danger);
21
+ ${(props) => props.$circle &&
22
+ css `
23
+ inset: -5px;
24
+ border-radius: 50%;
25
+ background: none;
26
+ box-shadow: inset 0 0 0 3.5px var(--ck-body-color-danger);
27
+ `}
28
+ }
29
+ `;
30
+
31
+ export { AnimationContainer, LoadingContainer };
32
+ //# sourceMappingURL=styles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"styles.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -1,14 +1,15 @@
1
1
  import { useState, useEffect } from 'react';
2
2
 
3
- function useDepositAddressOptions({ trpc, usdRequired, }) {
3
+ function useDepositAddressOptions({ trpc, usdRequired, mode, }) {
4
4
  const [options, setOptions] = useState([]);
5
5
  const [loading, setLoading] = useState(false);
6
6
  useEffect(() => {
7
- const refreshDepositAddressOptions = async () => {
7
+ const refreshDepositAddressOptions = async (usd, mode) => {
8
8
  setLoading(true);
9
9
  try {
10
10
  const options = await trpc.getDepositAddressOptions.query({
11
- usdRequired,
11
+ usdRequired: usd,
12
+ mode,
12
13
  });
13
14
  setOptions(options);
14
15
  }
@@ -19,8 +20,10 @@ function useDepositAddressOptions({ trpc, usdRequired, }) {
19
20
  setLoading(false);
20
21
  }
21
22
  };
22
- refreshDepositAddressOptions();
23
- }, [usdRequired]);
23
+ if (usdRequired != null && mode != null) {
24
+ refreshDepositAddressOptions(usdRequired, mode);
25
+ }
26
+ }, [usdRequired, mode]);
24
27
  return { options, loading };
25
28
  }
26
29
 
@@ -1 +1 @@
1
- {"version":3,"file":"useDepositAddressOptions.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"useDepositAddressOptions.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -7,18 +7,19 @@ const DEFAULT_EXTERNAL_PAYMENT_OPTIONS = [
7
7
  ExternalPaymentOptions.Daimo,
8
8
  ExternalPaymentOptions.RampNetwork,
9
9
  ];
10
- function useExternalPaymentOptions({ trpc, filterIds, usdRequired, platform, }) {
10
+ function useExternalPaymentOptions({ trpc, filterIds, platform, usdRequired, mode, }) {
11
11
  const [options, setOptions] = useState([]);
12
12
  const [loading, setLoading] = useState(false);
13
13
  useEffect(() => {
14
- const refreshExternalPaymentOptions = async (usd) => {
14
+ const refreshExternalPaymentOptions = async (usd, mode) => {
15
15
  if (!platform)
16
16
  return;
17
17
  setLoading(true);
18
18
  try {
19
19
  const newOptions = await trpc.getExternalPaymentOptions.query({
20
- usdRequired: usd,
21
20
  platform,
21
+ mode,
22
+ usdRequired: usd,
22
23
  });
23
24
  // Filter out options not in options JSON
24
25
  const enabledExtPaymentOptions = filterIds || DEFAULT_EXTERNAL_PAYMENT_OPTIONS;
@@ -32,10 +33,10 @@ function useExternalPaymentOptions({ trpc, filterIds, usdRequired, platform, })
32
33
  setLoading(false);
33
34
  }
34
35
  };
35
- if (usdRequired != null) {
36
- refreshExternalPaymentOptions(usdRequired);
36
+ if (usdRequired != null && mode != null) {
37
+ refreshExternalPaymentOptions(usdRequired, mode);
37
38
  }
38
- }, [usdRequired, filterIds, platform]);
39
+ }, [usdRequired, filterIds, platform, mode]);
39
40
  return {
40
41
  options,
41
42
  loading,
@@ -1 +1 @@
1
- {"version":3,"file":"useExternalPaymentOptions.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"useExternalPaymentOptions.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -0,0 +1,26 @@
1
+ import { useState, useEffect } from 'react';
2
+
3
+ function useOrderUsdLimits({ trpc }) {
4
+ const [limits, setLimits] = useState({});
5
+ const [loading, setLoading] = useState(false);
6
+ useEffect(() => {
7
+ const refreshOrderUsdLimits = async () => {
8
+ setLoading(true);
9
+ try {
10
+ const { limits: newLimits } = await trpc.getOrderUsdLimits.query();
11
+ setLimits(newLimits);
12
+ }
13
+ catch (e) {
14
+ console.error(e);
15
+ }
16
+ finally {
17
+ setLoading(false);
18
+ }
19
+ };
20
+ refreshOrderUsdLimits();
21
+ }, []);
22
+ return { limits, loading };
23
+ }
24
+
25
+ export { useOrderUsdLimits };
26
+ //# sourceMappingURL=useOrderUsdLimits.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useOrderUsdLimits.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -8,6 +8,7 @@ import { generatePayId } from '../utils/exports.js';
8
8
  import { detectPlatform } from '../utils/platform.js';
9
9
  import { useDepositAddressOptions } from './useDepositAddressOptions.js';
10
10
  import { useExternalPaymentOptions } from './useExternalPaymentOptions.js';
11
+ import { useOrderUsdLimits } from './useOrderUsdLimits.js';
11
12
  import { usePayWithSolanaToken } from './usePayWithSolanaToken.js';
12
13
  import { usePayWithToken } from './usePayWithToken.js';
13
14
  import { useSolanaPaymentOptions } from './useSolanaPaymentOptions.js';
@@ -31,14 +32,16 @@ function usePaymentState({ trpc, daimoPayOrder, setDaimoPayOrder, setOpen, log,
31
32
  // Daimo Pay order state.
32
33
  const [payParams, setPayParamsState] = useState();
33
34
  const [paymentWaitingMessage, setPaymentWaitingMessage] = useState();
35
+ const [isDepositFlow, setIsDepositFlow] = useState(false);
34
36
  // Payment UI config.
35
37
  const [modalOptions, setModalOptions] = useState({});
36
38
  // UI state. Selection for external payment (Binance, etc) vs wallet payment.
37
39
  const externalPaymentOptions = useExternalPaymentOptions({
38
40
  trpc,
39
41
  filterIds: daimoPayOrder?.metadata.payer?.paymentOptions,
40
- usdRequired: daimoPayOrder?.destFinalCallTokenAmount.usd,
41
42
  platform,
43
+ usdRequired: daimoPayOrder?.destFinalCallTokenAmount.usd,
44
+ mode: daimoPayOrder?.mode,
42
45
  });
43
46
  const walletPaymentOptions = useWalletPaymentOptions({
44
47
  trpc,
@@ -47,17 +50,21 @@ function usePaymentState({ trpc, daimoPayOrder, setDaimoPayOrder, setOpen, log,
47
50
  destChainId: daimoPayOrder?.destFinalCallTokenAmount.token.chainId,
48
51
  preferredChains: daimoPayOrder?.metadata.payer?.preferredChains,
49
52
  preferredTokens: daimoPayOrder?.metadata.payer?.preferredTokens,
53
+ isDepositFlow,
50
54
  log,
51
55
  });
52
56
  const solanaPaymentOptions = useSolanaPaymentOptions({
53
57
  trpc,
54
58
  address: solanaPubKey,
55
59
  usdRequired: daimoPayOrder?.destFinalCallTokenAmount.usd,
60
+ isDepositFlow,
56
61
  });
57
62
  const depositAddressOptions = useDepositAddressOptions({
58
63
  trpc,
59
- usdRequired: daimoPayOrder?.destFinalCallTokenAmount.usd ?? 0,
64
+ usdRequired: daimoPayOrder?.destFinalCallTokenAmount.usd,
65
+ mode: daimoPayOrder?.mode,
60
66
  });
67
+ const chainOrderUsdLimits = useOrderUsdLimits({ trpc });
61
68
  /** Create a new order or hydrate an existing one. */
62
69
  const createOrHydrate = async ({ order, refundAddress, externalPaymentOption, }) => {
63
70
  assert(!!platform, "missing platform");
@@ -72,15 +79,16 @@ function usePaymentState({ trpc, daimoPayOrder, setDaimoPayOrder, setOpen, log,
72
79
  });
73
80
  }
74
81
  log(`[CHECKOUT] creating+hydrating new order ${order.id}`);
75
- // Update units, if amountEditable the user may have changed the amount.
82
+ // Update units, if isDepositFlow then the user may have changed the amount.
76
83
  const toUnits = formatUnits(BigInt(order.destFinalCallTokenAmount.amount), order.destFinalCallTokenAmount.token.decimals);
77
84
  return await trpc.createOrder.mutate({
78
85
  appId: payParams.appId,
79
86
  paymentInput: {
80
87
  ...payParams,
81
88
  id: order.id.toString(),
82
- toUnits: toUnits,
89
+ toUnits,
83
90
  metadata: order.metadata,
91
+ isAmountEditable: isDepositFlow,
84
92
  },
85
93
  platform,
86
94
  refundAddress,
@@ -108,6 +116,16 @@ function usePaymentState({ trpc, daimoPayOrder, setDaimoPayOrder, setOpen, log,
108
116
  const [selectedTokenOption, setSelectedTokenOption] = useState();
109
117
  const [selectedSolanaTokenOption, setSelectedSolanaTokenOption] = useState();
110
118
  const [selectedDepositAddressOption, setSelectedDepositAddressOption] = useState();
119
+ const getOrderUsdLimit = () => {
120
+ const DEFAULT_USD_LIMIT = 20000;
121
+ if (daimoPayOrder == null || chainOrderUsdLimits.loading) {
122
+ return DEFAULT_USD_LIMIT;
123
+ }
124
+ const destChainId = daimoPayOrder.destFinalCallTokenAmount.token.chainId;
125
+ return destChainId in chainOrderUsdLimits.limits
126
+ ? chainOrderUsdLimits.limits[destChainId]
127
+ : DEFAULT_USD_LIMIT;
128
+ };
111
129
  const payWithExternal = async (option) => {
112
130
  assert(!!daimoPayOrder && !!platform);
113
131
  const { hydratedOrder, externalPaymentOptionData } = await createOrHydrate({
@@ -141,14 +159,17 @@ function usePaymentState({ trpc, daimoPayOrder, setDaimoPayOrder, setOpen, log,
141
159
  const { order } = await trpc.getOrder.query({
142
160
  id,
143
161
  });
144
- setDaimoPayOrder(order);
162
+ // Don't overwrite the order if a new order was generated.
163
+ if (daimoPayOrder == null || order.id === daimoPayOrder.id) {
164
+ setDaimoPayOrder(order);
165
+ }
145
166
  }, [daimoPayOrder?.id]);
146
167
  /** User picked a different deposit amount. */
147
- const setChosenUsd = (usdAmount) => {
148
- log(`[CHECKOUT] Setting chosen USD amount to ${usdAmount}`);
168
+ const setChosenUsd = (usd) => {
169
+ log(`[CHECKOUT] Setting chosen USD amount to ${usd}`);
149
170
  assert(!!daimoPayOrder);
150
171
  const token = daimoPayOrder.destFinalCallTokenAmount.token;
151
- const tokenAmount = parseUnits((usdAmount / token.usd).toString(), token.decimals);
172
+ const tokenAmount = parseUnits((usd / token.usd).toString(), token.decimals);
152
173
  // TODO: remove amount from destFinalCall, it is redundant with
153
174
  // destFinalCallTokenAmount. Here, we only modify one and not the other.
154
175
  setDaimoPayOrder({
@@ -156,7 +177,7 @@ function usePaymentState({ trpc, daimoPayOrder, setDaimoPayOrder, setOpen, log,
156
177
  destFinalCallTokenAmount: {
157
178
  token,
158
179
  amount: tokenAmount.toString(),
159
- usd: usdAmount,
180
+ usd: usd,
160
181
  },
161
182
  });
162
183
  };
@@ -180,16 +201,23 @@ function usePaymentState({ trpc, daimoPayOrder, setDaimoPayOrder, setOpen, log,
180
201
  const setPayParams = async (payParams) => {
181
202
  assert(payParams != null);
182
203
  setPayParamsState(payParams);
204
+ setIsDepositFlow(payParams.toUnits == null);
205
+ generatePreviewOrder(payParams);
206
+ };
207
+ const generatePreviewOrder = async (payParams) => {
183
208
  const newPayId = generatePayId();
184
209
  const newId = readDaimoPayOrderID(newPayId).toString();
185
- const payment = await trpc.previewOrder.query({
210
+ // toUnits is undefined if and only if we're in deposit flow.
211
+ // Set dummy value for deposit flow, since user can edit the amount.
212
+ const toUnits = payParams.toUnits == null ? "0" : payParams.toUnits;
213
+ const orderPreview = await trpc.previewOrder.query({
186
214
  id: newId,
187
215
  toChain: payParams.toChain,
188
216
  toToken: payParams.toToken,
189
- toUnits: payParams.toUnits,
217
+ toUnits,
190
218
  toAddress: payParams.toAddress,
191
219
  toCallData: payParams.toCallData,
192
- isAmountEditable: payParams.isAmountEditable,
220
+ isAmountEditable: payParams.toUnits == null,
193
221
  metadata: {
194
222
  intent: payParams.intent ?? "Pay",
195
223
  items: [],
@@ -200,7 +228,7 @@ function usePaymentState({ trpc, daimoPayOrder, setDaimoPayOrder, setOpen, log,
200
228
  },
201
229
  },
202
230
  });
203
- setDaimoPayOrder(payment);
231
+ setDaimoPayOrder(orderPreview);
204
232
  };
205
233
  const onSuccess = ({ txHash, txURL }) => {
206
234
  if (modalOptions?.closeOnSuccess) {
@@ -212,7 +240,9 @@ function usePaymentState({ trpc, daimoPayOrder, setDaimoPayOrder, setOpen, log,
212
240
  setPayId,
213
241
  payParams,
214
242
  setPayParams,
243
+ generatePreviewOrder,
215
244
  daimoPayOrder,
245
+ isDepositFlow,
216
246
  modalOptions,
217
247
  setModalOptions,
218
248
  paymentWaitingMessage,
@@ -224,10 +254,12 @@ function usePaymentState({ trpc, daimoPayOrder, setDaimoPayOrder, setOpen, log,
224
254
  solanaPaymentOptions,
225
255
  depositAddressOptions,
226
256
  selectedDepositAddressOption,
227
- setSelectedDepositAddressOption,
257
+ getOrderUsdLimit,
258
+ setPaymentWaitingMessage,
228
259
  setSelectedExternalOption,
229
260
  setSelectedTokenOption,
230
261
  setSelectedSolanaTokenOption,
262
+ setSelectedDepositAddressOption,
231
263
  setChosenUsd,
232
264
  payWithToken,
233
265
  payWithExternal,
@@ -1 +1 @@
1
- {"version":3,"file":"usePaymentState.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"usePaymentState.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -1,18 +1,20 @@
1
1
  import { useState, useEffect } from 'react';
2
2
 
3
- function useSolanaPaymentOptions({ trpc, address, usdRequired, }) {
3
+ /** Wallet payment options. User picks one. */
4
+ function useSolanaPaymentOptions({ trpc, address, usdRequired, isDepositFlow, }) {
4
5
  const [options, setOptions] = useState(null);
5
6
  const [isLoading, setIsLoading] = useState(false);
6
7
  useEffect(() => {
7
8
  const refreshWalletPaymentOptions = async () => {
8
- if (!address || !usdRequired)
9
+ if (address == null || usdRequired == null)
9
10
  return;
10
11
  setOptions(null);
11
12
  setIsLoading(true);
12
13
  try {
13
14
  const newOptions = await trpc.getSolanaPaymentOptions.query({
14
15
  pubKey: address,
15
- usdRequired,
16
+ // API expects undefined for deposit flow.
17
+ usdRequired: isDepositFlow ? undefined : usdRequired,
16
18
  });
17
19
  setOptions(newOptions);
18
20
  }
@@ -23,10 +25,10 @@ function useSolanaPaymentOptions({ trpc, address, usdRequired, }) {
23
25
  setIsLoading(false);
24
26
  }
25
27
  };
26
- if (address && usdRequired != null) {
28
+ if (address != null && usdRequired != null) {
27
29
  refreshWalletPaymentOptions();
28
30
  }
29
- }, [address, usdRequired]);
31
+ }, [address, usdRequired, isDepositFlow]);
30
32
  return {
31
33
  options,
32
34
  isLoading,
@@ -1 +1 @@
1
- {"version":3,"file":"useSolanaPaymentOptions.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"useSolanaPaymentOptions.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -1,25 +1,26 @@
1
- import { getDAv2Chains } from '@daimo/contract';
2
1
  import { useState, useEffect } from 'react';
2
+ import { supportedChainIds } from '../utils/exports.js';
3
3
 
4
- const supportedChainIds = new Set([...getDAv2Chains(false), ...getDAv2Chains(true)].map((c) => c.chainId));
5
- function useWalletPaymentOptions({ trpc, address, usdRequired, destChainId, preferredChains, preferredTokens, log, }) {
4
+ /** Wallet payment options. User picks one. */
5
+ function useWalletPaymentOptions({ trpc, address, usdRequired, destChainId, preferredChains, preferredTokens, isDepositFlow, log, }) {
6
6
  const [options, setOptions] = useState(null);
7
7
  const [isLoading, setIsLoading] = useState(false);
8
8
  useEffect(() => {
9
9
  const refreshWalletPaymentOptions = async () => {
10
- if (!address || !usdRequired || !destChainId)
10
+ if (address == null || usdRequired == null || destChainId == null)
11
11
  return;
12
12
  setOptions(null);
13
13
  setIsLoading(true);
14
14
  try {
15
15
  const newOptions = await trpc.getWalletPaymentOptions.query({
16
16
  payerAddress: address,
17
- usdRequired,
17
+ // API expects undefined for deposit flow.
18
+ usdRequired: isDepositFlow ? undefined : usdRequired,
18
19
  destChainId,
19
20
  preferredChains,
20
21
  preferredTokens,
21
22
  });
22
- // Filter out options we don't support yet.
23
+ // Filter out chains we don't support yet.
23
24
  const isSupported = (o) => supportedChainIds.has(o.balance.token.chainId);
24
25
  const filteredOptions = newOptions.filter(isSupported);
25
26
  if (filteredOptions.length < newOptions.length) {
@@ -34,10 +35,10 @@ function useWalletPaymentOptions({ trpc, address, usdRequired, destChainId, pref
34
35
  setIsLoading(false);
35
36
  }
36
37
  };
37
- if (address && usdRequired != null && destChainId) {
38
+ if (address != null && usdRequired != null && destChainId != null) {
38
39
  refreshWalletPaymentOptions();
39
40
  }
40
- }, [address, usdRequired, destChainId]);
41
+ }, [address, usdRequired, destChainId, isDepositFlow]);
41
42
  return {
42
43
  options,
43
44
  isLoading,
@@ -1 +1 @@
1
- {"version":3,"file":"useWalletPaymentOptions.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"useWalletPaymentOptions.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -7,5 +7,5 @@ export { useModal as useDaimoPayModal } from './hooks/useModal.js';
7
7
  export { default as Avatar } from './components/Common/Avatar/index.js';
8
8
  export { default as ChainIcon } from './components/Common/Chain/index.js';
9
9
  export { wallets } from './wallets/index.js';
10
- export { daimoPayVersion, generatePayId } from './utils/exports.js';
10
+ export { daimoPayVersion, generatePayId, supportedChainIds } from './utils/exports.js';
11
11
  //# sourceMappingURL=index.js.map
@@ -1,4 +1,5 @@
1
1
  import { writeDaimoPayOrderID } from '@daimo/common';
2
+ import { getDAv2Chains } from '@daimo/contract';
2
3
  import { bytesToBigInt } from 'viem';
3
4
  import packageJson from '../../package.json.js';
4
5
 
@@ -9,6 +10,8 @@ function generatePayId() {
9
10
  const id = bytesToBigInt(crypto.getRandomValues(new Uint8Array(32)));
10
11
  return writeDaimoPayOrderID(id);
11
12
  }
13
+ /** Chain ids supported by Daimo Pay. */
14
+ const supportedChainIds = new Set([...getDAv2Chains(false), ...getDAv2Chains(true)].map((c) => c.chainId));
12
15
 
13
- export { daimoPayVersion, generatePayId };
16
+ export { daimoPayVersion, generatePayId, supportedChainIds };
14
17
  //# sourceMappingURL=exports.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"exports.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"exports.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;"}
@@ -0,0 +1,83 @@
1
+ import { formatUnits } from 'viem';
2
+
3
+ const USD_DECIMALS = 2;
4
+ /**
5
+ * Round a number to a given number of decimal places
6
+ *
7
+ * @param round - The rounding strategy to use:
8
+ * - "up": Always rounds up to the next decimal place (ceiling)
9
+ * - "down": Always rounds down to the previous decimal place (floor)
10
+ * - "nearest": Rounds to the nearest decimal place (standard rounding)
11
+ */
12
+ function roundDecimals(value, decimals, round) {
13
+ const factor = 10 ** decimals;
14
+ const multiplied = value * factor;
15
+ let rounded;
16
+ if (round === "up") {
17
+ rounded = Math.ceil(multiplied);
18
+ }
19
+ else if (round === "down") {
20
+ rounded = Math.floor(multiplied);
21
+ }
22
+ else {
23
+ rounded = Math.round(multiplied);
24
+ }
25
+ return (rounded / factor).toFixed(decimals);
26
+ }
27
+ /**
28
+ * Format a number as a USD amount
29
+ *
30
+ * @param usd - The USD amount to format
31
+ * @param round - The rounding strategy to use ("up", "down", or "nearest")
32
+ * @returns The formatted USD amount
33
+ */
34
+ function formatUsd(usd, round = "down") {
35
+ return new Intl.NumberFormat("en-US", {
36
+ style: "currency",
37
+ currency: "USD",
38
+ }).format(Number(roundUsd(usd, round)));
39
+ }
40
+ /**
41
+ * Round a USD amount to `USD_DECIMALS` precision
42
+ */
43
+ function roundUsd(usd, round = "down") {
44
+ return roundDecimals(usd, USD_DECIMALS, round);
45
+ }
46
+ /**
47
+ * Round a token amount to `displayDecimals` precision
48
+ */
49
+ function roundTokenAmount(amount, token, round = "down") {
50
+ return roundDecimals(Number(formatUnits(BigInt(amount), token.decimals)), token.displayDecimals, round);
51
+ }
52
+ /**
53
+ * Round a token amount in units to `displayDecimals` precision
54
+ */
55
+ function roundTokenAmountUnits(amountUnits, token, round = "down") {
56
+ return roundDecimals(amountUnits, token.displayDecimals, round);
57
+ }
58
+ /**
59
+ * Convert a USD amount to a token amount with `displayDecimals` precision
60
+ *
61
+ * @param usd - The USD amount to convert
62
+ * @param token - The token to convert to
63
+ * @param round - The rounding strategy to use ("up", "down", or "nearest")
64
+ * @returns The token amount
65
+ */
66
+ function usdToRoundedTokenAmount(usd, token, round = "down") {
67
+ return roundTokenAmountUnits(usd / token.usd, token, round);
68
+ }
69
+ /**
70
+ * Convert a token amount to a USD amount with `USD_DECIMALS` precision
71
+ *
72
+ * @param amount - The token amount to convert
73
+ * @param token - The token to convert from
74
+ * @param round - The rounding strategy to use ("up", "down", or "nearest")
75
+ * @returns The formatted USD amount
76
+ */
77
+ function tokenAmountToRoundedUsd(amount, token, round = "nearest") {
78
+ const amountUnits = formatUnits(BigInt(amount), token.decimals);
79
+ return roundUsd(Number(amountUnits) * token.usd, round);
80
+ }
81
+
82
+ export { USD_DECIMALS, formatUsd, roundDecimals, roundTokenAmount, roundTokenAmountUnits, roundUsd, tokenAmountToRoundedUsd, usdToRoundedTokenAmount };
83
+ //# sourceMappingURL=format.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Returns true if the value is a valid number.
3
+ * @param value The value to validate.
4
+ * @param maxDecimals The maximum number of decimal places.
5
+ * @returns True if the value is a valid number, false otherwise.
6
+ */
7
+ function isValidNumber(value, maxDecimals) {
8
+ if (value.length === 0) {
9
+ return false;
10
+ }
11
+ // Test that the value is digits, followed by an optional decimal, followed
12
+ // by more digits
13
+ const match = /^\d*\.?(\d*)$/.exec(value);
14
+ if (match == null)
15
+ return false;
16
+ // Check that the number of digits after the decimal is less than or equal to
17
+ // maxDecimals
18
+ return maxDecimals == null || match[1].length <= maxDecimals;
19
+ }
20
+ /**
21
+ * Sanitize a number input.
22
+ * - Remove all non-numeric and non-decimal characters
23
+ * - If the value is empty, return "0"
24
+ * - Parse into a number
25
+ * @param value The value to sanitize.
26
+ * @returns The sanitized value.
27
+ */
28
+ function sanitizeNumber(value) {
29
+ const cleaned = value.replace(/[^0-9.]/g, "");
30
+ if (!cleaned || cleaned === "." || !/^\d*\.?\d*$/.test(cleaned))
31
+ return "0";
32
+ return cleaned.startsWith(".") ? `0${cleaned}` : cleaned;
33
+ }
34
+
35
+ export { isValidNumber, sanitizeNumber };
36
+ //# sourceMappingURL=validateInput.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validateInput.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@daimo/pay",
3
3
  "private": false,
4
- "version": "1.1.4",
4
+ "version": "1.2.0",
5
5
  "author": "Daimo",
6
6
  "homepage": "https://pay.daimo.com",
7
7
  "license": "BSD-2-Clause license",
@@ -40,8 +40,8 @@
40
40
  "crypto"
41
41
  ],
42
42
  "dependencies": {
43
- "@daimo/common": "0.3.18",
44
- "@daimo/contract": "0.3.18",
43
+ "@daimo/common": "1.1.1",
44
+ "@daimo/contract": "1.1.1",
45
45
  "@solana/wallet-adapter-base": "^0.9.23",
46
46
  "@solana/wallet-adapter-react": "^0.15.35",
47
47
  "@solana/web3.js": "^1.95.4",
@@ -67,7 +67,6 @@
67
67
  },
68
68
  "devDependencies": {
69
69
  "@rollup/plugin-json": "^6.1.0",
70
- "@rollup/plugin-node-resolve": "^13.1.3",
71
70
  "@types/node": "^20.14.12",
72
71
  "@types/qrcode": "^1.4.2",
73
72
  "@types/react": "^18.2.47",