@daimo/pay 1.1.5 → 1.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.
Files changed (96) hide show
  1. package/build/index.d.ts +56 -122
  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 +147 -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 +49 -14
  25. package/build/src/components/DaimoPay.js.map +1 -1
  26. package/build/src/components/DaimoPayButton/index.js +31 -25
  27. package/build/src/components/DaimoPayButton/index.js.map +1 -1
  28. package/build/src/components/DaimoPayModal/index.js +102 -36
  29. package/build/src/components/DaimoPayModal/index.js.map +1 -1
  30. package/build/src/components/Pages/Confirmation/index.js +1 -1
  31. package/build/src/components/Pages/PayWithToken/index.js +5 -110
  32. package/build/src/components/Pages/PayWithToken/index.js.map +1 -1
  33. package/build/src/components/Pages/SelectAmount/index.js +16 -0
  34. package/build/src/components/Pages/SelectAmount/index.js.map +1 -0
  35. package/build/src/components/Pages/SelectDepositAddressAmount/index.js +61 -0
  36. package/build/src/components/Pages/SelectDepositAddressAmount/index.js.map +1 -0
  37. package/build/src/components/Pages/SelectDepositAddressChain/index.js +8 -2
  38. package/build/src/components/Pages/SelectDepositAddressChain/index.js.map +1 -1
  39. package/build/src/components/Pages/SelectExternalAmount/index.js +63 -0
  40. package/build/src/components/Pages/SelectExternalAmount/index.js.map +1 -0
  41. package/build/src/components/Pages/SelectMethod/index.js +22 -9
  42. package/build/src/components/Pages/SelectMethod/index.js.map +1 -1
  43. package/build/src/components/Pages/SelectToken/index.js +41 -41
  44. package/build/src/components/Pages/Solana/ConnectSolana/index.js +5 -2
  45. package/build/src/components/Pages/Solana/ConnectSolana/index.js.map +1 -1
  46. package/build/src/components/Pages/Solana/ConnectorSolana/index.js +13 -3
  47. package/build/src/components/Pages/Solana/ConnectorSolana/index.js.map +1 -1
  48. package/build/src/components/Pages/Solana/PayWithSolanaToken/index.js +13 -36
  49. package/build/src/components/Pages/Solana/PayWithSolanaToken/index.js.map +1 -1
  50. package/build/src/components/Pages/Solana/SelectSolanaAmount/index.js +16 -0
  51. package/build/src/components/Pages/Solana/SelectSolanaAmount/index.js.map +1 -0
  52. package/build/src/components/Pages/Solana/SelectSolanaToken/index.js +42 -21
  53. package/build/src/components/Pages/Solana/SelectSolanaToken/index.js.map +1 -1
  54. package/build/src/components/Pages/WaitingDepositAddress/index.js +1 -1
  55. package/build/src/components/Pages/WaitingExternal/index.js +53 -0
  56. package/build/src/components/Pages/WaitingExternal/index.js.map +1 -0
  57. package/build/src/components/Spinners/ExternalPaymentSpinner/index.js +20 -0
  58. package/build/src/components/Spinners/ExternalPaymentSpinner/index.js.map +1 -0
  59. package/build/src/components/Spinners/TokenLogoSpinner/index.js +13 -0
  60. package/build/src/components/Spinners/TokenLogoSpinner/index.js.map +1 -0
  61. package/build/src/components/Spinners/TokenLogoSpinner/styles.js +40 -0
  62. package/build/src/components/Spinners/TokenLogoSpinner/styles.js.map +1 -0
  63. package/build/src/components/Spinners/styles.js +32 -0
  64. package/build/src/components/Spinners/styles.js.map +1 -0
  65. package/build/src/hooks/useDaimoPayStatus.js +3 -16
  66. package/build/src/hooks/useDaimoPayStatus.js.map +1 -1
  67. package/build/src/hooks/useDepositAddressOptions.js +8 -5
  68. package/build/src/hooks/useDepositAddressOptions.js.map +1 -1
  69. package/build/src/hooks/useExternalPaymentOptions.js +7 -6
  70. package/build/src/hooks/useExternalPaymentOptions.js.map +1 -1
  71. package/build/src/hooks/useOrderUsdLimits.js +26 -0
  72. package/build/src/hooks/useOrderUsdLimits.js.map +1 -0
  73. package/build/src/hooks/usePayWithSolanaToken.js +4 -3
  74. package/build/src/hooks/usePayWithSolanaToken.js.map +1 -1
  75. package/build/src/hooks/usePayWithToken.js +7 -8
  76. package/build/src/hooks/usePayWithToken.js.map +1 -1
  77. package/build/src/hooks/usePaymentState.js +71 -32
  78. package/build/src/hooks/usePaymentState.js.map +1 -1
  79. package/build/src/hooks/useSolanaPaymentOptions.js +7 -5
  80. package/build/src/hooks/useSolanaPaymentOptions.js.map +1 -1
  81. package/build/src/hooks/useWalletPaymentOptions.js +9 -8
  82. package/build/src/hooks/useWalletPaymentOptions.js.map +1 -1
  83. package/build/src/index.js +1 -2
  84. package/build/src/index.js.map +1 -1
  85. package/build/src/utils/exports.js +4 -8
  86. package/build/src/utils/exports.js.map +1 -1
  87. package/build/src/utils/format.js +83 -0
  88. package/build/src/utils/format.js.map +1 -0
  89. package/build/src/utils/trpc.js +2 -1
  90. package/build/src/utils/trpc.js.map +1 -1
  91. package/build/src/utils/validateInput.js +36 -0
  92. package/build/src/utils/validateInput.js.map +1 -0
  93. package/package.json +3 -4
  94. package/build/src/components/Pages/WaitingOther/index.js +0 -91
  95. package/build/src/hooks/useModal.js +0 -35
  96. package/build/src/hooks/useModal.js.map +0 -1
@@ -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":";;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -7,8 +7,9 @@ function usePayWithSolanaToken({ trpc, daimoPayOrder, setDaimoPayOrder, createOr
7
7
  const { connection } = useConnection();
8
8
  const wallet = useWallet();
9
9
  const payWithSolanaToken = async (inputToken) => {
10
- assert(!!wallet.publicKey, "No wallet connected");
11
- assert(!!platform && !!daimoPayOrder);
10
+ assert(!!wallet.publicKey, "[PAY SOLANA] No wallet connected");
11
+ assert(!!daimoPayOrder, "[PAY SOLANA] daimoPayOrder cannot be null");
12
+ assert(!!platform, "[PAY SOLANA] platform cannot be null");
12
13
  const orderId = daimoPayOrder.id;
13
14
  const { hydratedOrder } = await createOrHydrate({
14
15
  order: daimoPayOrder,
@@ -18,7 +19,7 @@ function usePayWithSolanaToken({ trpc, daimoPayOrder, setDaimoPayOrder, createOr
18
19
  try {
19
20
  const serializedTx = await trpc.getSolanaSwapAndBurnTx.query({
20
21
  orderId: orderId.toString(),
21
- userPublicKey: assertNotNull(wallet.publicKey).toString(),
22
+ userPublicKey: assertNotNull(wallet.publicKey, "[PAY SOLANA] wallet.publicKey cannot be null").toString(),
22
23
  inputTokenMint: inputToken,
23
24
  });
24
25
  const tx = VersionedTransaction.deserialize(hexToBytes(serializedTx));
@@ -1 +1 @@
1
- {"version":3,"file":"usePayWithSolanaToken.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"usePayWithSolanaToken.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -7,12 +7,14 @@ function usePayWithToken({ trpc, senderAddr, daimoPayOrder, setDaimoPayOrder, cr
7
7
  const { sendTransactionAsync } = useSendTransaction();
8
8
  /** Commit to a token + amount = initiate payment. */
9
9
  const payWithToken = async (tokenAmount) => {
10
- assert(!!daimoPayOrder && !!platform);
10
+ assert(!!daimoPayOrder, "[PAY TOKEN] daimoPayOrder cannot be null");
11
+ assert(!!platform, "[PAY TOKEN] platform cannot be null");
11
12
  const { hydratedOrder } = await createOrHydrate({
12
13
  order: daimoPayOrder,
13
14
  refundAddress: senderAddr,
14
15
  });
15
- log(`[CHECKOUT] Hydrated order: ${JSON.stringify(hydratedOrder)}, checking out with ${tokenAmount.token.token}`);
16
+ log(`[CHECKOUT] hydrated order: ${JSON.stringify(hydratedOrder)}, checking out with ${tokenAmount.token.token}`);
17
+ setDaimoPayOrder(hydratedOrder);
16
18
  const txHash = await (async () => {
17
19
  try {
18
20
  if (tokenAmount.token.token === zeroAddress) {
@@ -31,23 +33,20 @@ function usePayWithToken({ trpc, senderAddr, daimoPayOrder, setDaimoPayOrder, cr
31
33
  }
32
34
  }
33
35
  catch (e) {
34
- console.error(`[CHECKOUT] Error sending token: ${e}`);
35
- setDaimoPayOrder(hydratedOrder);
36
+ console.error(`[CHECKOUT] error sending token: ${e}`);
36
37
  throw e;
37
38
  }
38
- finally {
39
- setDaimoPayOrder(hydratedOrder);
40
- }
41
39
  })();
42
40
  if (txHash) {
43
41
  await trpc.processSourcePayment.mutate({
44
42
  orderId: daimoPayOrder.id.toString(),
45
43
  sourceInitiateTxHash: txHash,
46
44
  sourceChainId: tokenAmount.token.chainId,
47
- sourceFulfillerAddr: assertNotNull(senderAddr),
45
+ sourceFulfillerAddr: assertNotNull(senderAddr, `[PAY TOKEN] senderAddr cannot be null on order ${daimoPayOrder.id}`),
48
46
  sourceToken: tokenAmount.token.token,
49
47
  sourceAmount: tokenAmount.amount,
50
48
  });
49
+ // TODO: update order immediately, do not wait for polling.
51
50
  }
52
51
  };
53
52
  return { payWithToken };
@@ -1 +1 @@
1
- {"version":3,"file":"usePayWithToken.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"usePayWithToken.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -4,10 +4,10 @@ import { useWallet } from '@solana/wallet-adapter-react';
4
4
  import { useState, useEffect, useCallback } from 'react';
5
5
  import { parseUnits, formatUnits } from 'viem';
6
6
  import { useAccount, useEnsName } from 'wagmi';
7
- import { generatePayId } from '../utils/exports.js';
8
7
  import { detectPlatform } from '../utils/platform.js';
9
8
  import { useDepositAddressOptions } from './useDepositAddressOptions.js';
10
9
  import { useExternalPaymentOptions } from './useExternalPaymentOptions.js';
10
+ import { useOrderUsdLimits } from './useOrderUsdLimits.js';
11
11
  import { usePayWithSolanaToken } from './usePayWithSolanaToken.js';
12
12
  import { usePayWithToken } from './usePayWithToken.js';
13
13
  import { useSolanaPaymentOptions } from './useSolanaPaymentOptions.js';
@@ -31,14 +31,16 @@ function usePaymentState({ trpc, daimoPayOrder, setDaimoPayOrder, setOpen, log,
31
31
  // Daimo Pay order state.
32
32
  const [payParams, setPayParamsState] = useState();
33
33
  const [paymentWaitingMessage, setPaymentWaitingMessage] = useState();
34
+ const [isDepositFlow, setIsDepositFlow] = useState(false);
34
35
  // Payment UI config.
35
36
  const [modalOptions, setModalOptions] = useState({});
36
37
  // UI state. Selection for external payment (Binance, etc) vs wallet payment.
37
38
  const externalPaymentOptions = useExternalPaymentOptions({
38
39
  trpc,
39
40
  filterIds: daimoPayOrder?.metadata.payer?.paymentOptions,
40
- usdRequired: daimoPayOrder?.destFinalCallTokenAmount.usd,
41
41
  platform,
42
+ usdRequired: daimoPayOrder?.destFinalCallTokenAmount.usd,
43
+ mode: daimoPayOrder?.mode,
42
44
  });
43
45
  const walletPaymentOptions = useWalletPaymentOptions({
44
46
  trpc,
@@ -47,22 +49,26 @@ function usePaymentState({ trpc, daimoPayOrder, setDaimoPayOrder, setOpen, log,
47
49
  destChainId: daimoPayOrder?.destFinalCallTokenAmount.token.chainId,
48
50
  preferredChains: daimoPayOrder?.metadata.payer?.preferredChains,
49
51
  preferredTokens: daimoPayOrder?.metadata.payer?.preferredTokens,
52
+ isDepositFlow,
50
53
  log,
51
54
  });
52
55
  const solanaPaymentOptions = useSolanaPaymentOptions({
53
56
  trpc,
54
57
  address: solanaPubKey,
55
58
  usdRequired: daimoPayOrder?.destFinalCallTokenAmount.usd,
59
+ isDepositFlow,
56
60
  });
57
61
  const depositAddressOptions = useDepositAddressOptions({
58
62
  trpc,
59
- usdRequired: daimoPayOrder?.destFinalCallTokenAmount.usd ?? 0,
63
+ usdRequired: daimoPayOrder?.destFinalCallTokenAmount.usd,
64
+ mode: daimoPayOrder?.mode,
60
65
  });
66
+ const chainOrderUsdLimits = useOrderUsdLimits({ trpc });
61
67
  /** Create a new order or hydrate an existing one. */
62
68
  const createOrHydrate = async ({ order, refundAddress, externalPaymentOption, }) => {
63
- assert(!!platform, "missing platform");
69
+ assert(!!platform, "[CREATE/HYDRATE] missing platform");
64
70
  if (payParams == null) {
65
- log(`[CHECKOUT] hydrating existing order ${order.id}`);
71
+ log(`[CREATE/HYDRATE] hydrating existing order ${order.id}`);
66
72
  return await trpc.hydrateOrder.query({
67
73
  id: order.id.toString(),
68
74
  chosenFinalTokenAmount: order.destFinalCallTokenAmount.amount,
@@ -71,16 +77,18 @@ function usePaymentState({ trpc, daimoPayOrder, setDaimoPayOrder, setOpen, log,
71
77
  externalPaymentOption,
72
78
  });
73
79
  }
74
- log(`[CHECKOUT] creating+hydrating new order ${order.id}`);
75
- // Update units, if amountEditable the user may have changed the amount.
80
+ log(`[CREATE/HYDRATE] creating+hydrating new order ${order.id}`);
81
+ // Update units, if isDepositFlow then the user may have changed the amount.
76
82
  const toUnits = formatUnits(BigInt(order.destFinalCallTokenAmount.amount), order.destFinalCallTokenAmount.token.decimals);
77
83
  return await trpc.createOrder.mutate({
78
84
  appId: payParams.appId,
79
85
  paymentInput: {
80
86
  ...payParams,
81
87
  id: order.id.toString(),
82
- toUnits: toUnits,
88
+ toUnits,
83
89
  metadata: order.metadata,
90
+ userMetadata: payParams.metadata,
91
+ isAmountEditable: isDepositFlow,
84
92
  },
85
93
  platform,
86
94
  refundAddress,
@@ -108,29 +116,40 @@ 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
- assert(!!daimoPayOrder && !!platform);
130
+ assert(!!daimoPayOrder, "[PAY EXTERNAL] daimoPayOrder cannot be null");
131
+ assert(!!platform, "[PAY EXTERNAL] platform cannot be null");
113
132
  const { hydratedOrder, externalPaymentOptionData } = await createOrHydrate({
114
133
  order: daimoPayOrder,
115
134
  externalPaymentOption: option,
116
135
  });
117
- assert(!!externalPaymentOptionData, "missing externalPaymentOptionData");
118
- log(`[CHECKOUT] Hydrated order: ${JSON.stringify(hydratedOrder)}, checking out with external payment: ${option}`);
136
+ assert(!!externalPaymentOptionData, "[PAY EXTERNAL] missing externalPaymentOptionData");
137
+ log(`[PAY EXTERNAL] hydrated order: ${JSON.stringify(hydratedOrder)}, checking out with external payment: ${option}`);
119
138
  setPaymentWaitingMessage(externalPaymentOptionData.waitingMessage);
120
139
  setDaimoPayOrder(hydratedOrder);
121
140
  return externalPaymentOptionData.url;
122
141
  };
123
142
  const payWithDepositAddress = async (option) => {
124
- assert(!!daimoPayOrder);
143
+ assert(!!daimoPayOrder, "[PAY DEPOSIT ADDRESS] missing daimoPayOrder");
125
144
  const { hydratedOrder } = await createOrHydrate({
126
145
  order: daimoPayOrder,
127
146
  });
128
147
  setDaimoPayOrder(hydratedOrder);
129
- log(`[CHECKOUT] Hydrated order: ${JSON.stringify(hydratedOrder)}, checking out with deposit address: ${option}`);
148
+ log(`[PAY DEPOSIT ADDRESS] hydrated order: ${JSON.stringify(hydratedOrder)}, checking out with deposit address: ${option}`);
130
149
  const depositAddressOption = await trpc.getDepositAddressOptionData.query({
131
150
  input: option,
132
151
  usdRequired: daimoPayOrder.destFinalCallTokenAmount.usd,
133
- toAddress: assertNotNull(hydratedOrder.intentAddr),
152
+ toAddress: assertNotNull(hydratedOrder.intentAddr, `[PAY DEPOSIT ADDRESS] missing intentAddr on order ${hydratedOrder.id}`),
134
153
  });
135
154
  return depositAddressOption;
136
155
  };
@@ -141,22 +160,30 @@ function usePaymentState({ trpc, daimoPayOrder, setDaimoPayOrder, setOpen, log,
141
160
  const { order } = await trpc.getOrder.query({
142
161
  id,
143
162
  });
144
- setDaimoPayOrder(order);
163
+ // Don't overwrite the order if a new order was generated.
164
+ if (daimoPayOrder == null || order.id === daimoPayOrder.id) {
165
+ log(`[CHECKOUT] refreshed order: ${order.id}`);
166
+ setDaimoPayOrder(order);
167
+ }
168
+ else {
169
+ log(`[CHECKOUT] IGNORING refreshOrder, wrong ID: ${order.id} vs ${daimoPayOrder.id}`);
170
+ }
145
171
  }, [daimoPayOrder?.id]);
146
172
  /** User picked a different deposit amount. */
147
- const setChosenUsd = (usdAmount) => {
148
- log(`[CHECKOUT] Setting chosen USD amount to ${usdAmount}`);
149
- assert(!!daimoPayOrder);
173
+ const setChosenUsd = (usd) => {
174
+ assert(!!daimoPayOrder, "[SET CHOSEN USD] daimoPayOrder cannot be null");
150
175
  const token = daimoPayOrder.destFinalCallTokenAmount.token;
151
- const tokenAmount = parseUnits((usdAmount / token.usd).toString(), token.decimals);
176
+ const tokenUnits = (usd / token.usd).toString();
177
+ const tokenAmount = parseUnits(tokenUnits, token.decimals);
152
178
  // TODO: remove amount from destFinalCall, it is redundant with
153
179
  // destFinalCallTokenAmount. Here, we only modify one and not the other.
180
+ log(`[CHECKOUT] setting chosen USD amount to $${usd} = ${tokenUnits} ${token.symbol}`);
154
181
  setDaimoPayOrder({
155
182
  ...daimoPayOrder,
156
183
  destFinalCallTokenAmount: {
157
184
  token,
158
185
  amount: tokenAmount.toString(),
159
- usd: usdAmount,
186
+ usd: usd,
160
187
  },
161
188
  });
162
189
  };
@@ -170,26 +197,31 @@ function usePaymentState({ trpc, daimoPayOrder, setDaimoPayOrder, setOpen, log,
170
197
  }
171
198
  const { order } = await trpc.getOrder.query({ id });
172
199
  if (!order) {
173
- console.error(`[CHECKOUT] No order found for ${payId}`);
200
+ console.error(`[CHECKOUT] setPayId: no order found for ${payId}`);
174
201
  return;
175
202
  }
176
- log(`[CHECKOUT] fetched order: ${JSON.stringify(order)}`);
203
+ log(`[CHECKOUT] setPayId: fetched order: ${JSON.stringify(order)}`);
177
204
  setDaimoPayOrder(order);
178
205
  }, [daimoPayOrder]);
179
206
  /** Called whenever params change. */
180
207
  const setPayParams = async (payParams) => {
181
- assert(payParams != null);
208
+ assert(payParams != null, "[SET PAY PARAMS] payParams cannot be null");
182
209
  setPayParamsState(payParams);
183
- const newPayId = generatePayId();
184
- const newId = readDaimoPayOrderID(newPayId).toString();
185
- const payment = await trpc.previewOrder.query({
186
- id: newId,
210
+ setIsDepositFlow(payParams.toUnits == null);
211
+ generatePreviewOrder(payParams);
212
+ };
213
+ const generatePreviewOrder = async (payParams) => {
214
+ // toUnits is undefined if and only if we're in deposit flow.
215
+ // Set dummy value for deposit flow, since user can edit the amount.
216
+ const toUnits = payParams.toUnits == null ? "0" : payParams.toUnits;
217
+ const orderPreview = await trpc.previewOrder.query({
218
+ appId: payParams.appId,
187
219
  toChain: payParams.toChain,
188
220
  toToken: payParams.toToken,
189
- toUnits: payParams.toUnits,
221
+ toUnits,
190
222
  toAddress: payParams.toAddress,
191
223
  toCallData: payParams.toCallData,
192
- isAmountEditable: payParams.isAmountEditable,
224
+ isAmountEditable: payParams.toUnits == null,
193
225
  metadata: {
194
226
  intent: payParams.intent ?? "Pay",
195
227
  items: [],
@@ -199,20 +231,25 @@ function usePaymentState({ trpc, daimoPayOrder, setDaimoPayOrder, setOpen, log,
199
231
  preferredTokens: payParams.preferredTokens,
200
232
  },
201
233
  },
234
+ externalId: payParams.externalId,
235
+ userMetadata: payParams.metadata,
202
236
  });
203
- setDaimoPayOrder(payment);
237
+ log(`[CHECKOUT] generated preview: ${JSON.stringify(orderPreview)}`);
238
+ setDaimoPayOrder(orderPreview);
204
239
  };
205
240
  const onSuccess = ({ txHash, txURL }) => {
206
241
  if (modalOptions?.closeOnSuccess) {
207
242
  log(`[CHECKOUT] transaction succeeded, closing: ${txHash} ${txURL}`);
208
- setTimeout(() => setOpen(false), 1000);
243
+ setTimeout(() => setOpen(false, { event: "wait-success" }), 1000);
209
244
  }
210
245
  };
211
246
  return {
212
247
  setPayId,
213
248
  payParams,
214
249
  setPayParams,
250
+ generatePreviewOrder,
215
251
  daimoPayOrder,
252
+ isDepositFlow,
216
253
  modalOptions,
217
254
  setModalOptions,
218
255
  paymentWaitingMessage,
@@ -224,10 +261,12 @@ function usePaymentState({ trpc, daimoPayOrder, setDaimoPayOrder, setOpen, log,
224
261
  solanaPaymentOptions,
225
262
  depositAddressOptions,
226
263
  selectedDepositAddressOption,
227
- setSelectedDepositAddressOption,
264
+ getOrderUsdLimit,
265
+ setPaymentWaitingMessage,
228
266
  setSelectedExternalOption,
229
267
  setSelectedTokenOption,
230
268
  setSelectedSolanaTokenOption,
269
+ setSelectedDepositAddressOption,
231
270
  setChosenUsd,
232
271
  payWithToken,
233
272
  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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -3,9 +3,8 @@ export { Context as DaimoPayContext, DaimoPayProvider, usePayContext } from './c
3
3
  export { default as getDefaultConfig } from './defaultConfig.js';
4
4
  export { DaimoPayButton } from './components/DaimoPayButton/index.js';
5
5
  export { useDaimoPayStatus } from './hooks/useDaimoPayStatus.js';
6
- export { useModal as useDaimoPayModal } from './hooks/useModal.js';
7
6
  export { default as Avatar } from './components/Common/Avatar/index.js';
8
7
  export { default as ChainIcon } from './components/Common/Chain/index.js';
9
8
  export { wallets } from './wallets/index.js';
10
- export { daimoPayVersion, generatePayId } from './utils/exports.js';
9
+ export { daimoPayVersion, supportedChainIds } from './utils/exports.js';
11
10
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;"}
@@ -1,14 +1,10 @@
1
- import { writeDaimoPayOrderID } from '@daimo/common';
2
- import { bytesToBigInt } from 'viem';
1
+ import { getDAv2Chains } from '@daimo/contract';
3
2
  import packageJson from '../../package.json.js';
4
3
 
5
4
  // Exported utilities, useful for @daimo/pay users.
6
5
  const daimoPayVersion = packageJson.version;
7
- /** Generates a globally-unique payId. */
8
- function generatePayId() {
9
- const id = bytesToBigInt(crypto.getRandomValues(new Uint8Array(32)));
10
- return writeDaimoPayOrderID(id);
11
- }
6
+ /** Chain ids supported by Daimo Pay. */
7
+ const supportedChainIds = new Set([...getDAv2Chains(false), ...getDAv2Chains(true)].map((c) => c.chainId));
12
8
 
13
- export { daimoPayVersion, generatePayId };
9
+ export { daimoPayVersion, supportedChainIds };
14
10
  //# 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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -1,13 +1,14 @@
1
1
  import { createTRPCClient, httpBatchLink } from '@trpc/client';
2
2
  import { daimoPayVersion } from './exports.js';
3
3
 
4
- function createTrpcClient(apiUrl) {
4
+ function createTrpcClient(apiUrl, sessionId) {
5
5
  return createTRPCClient({
6
6
  links: [
7
7
  httpBatchLink({
8
8
  url: apiUrl,
9
9
  headers: {
10
10
  "x-pay-version": daimoPayVersion,
11
+ "x-session-id": sessionId,
11
12
  },
12
13
  }),
13
14
  ],
@@ -1 +1 @@
1
- {"version":3,"file":"trpc.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"trpc.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;"}