@daimo/pay 1.3.3 → 1.4.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 (27) hide show
  1. package/build/index.d.ts +20 -5
  2. package/build/package.json.js +3 -3
  3. package/build/src/components/Common/PaymentBreakdown/index.js +2 -2
  4. package/build/src/components/Common/PoweredByFooter/index.js +4 -1
  5. package/build/src/components/Common/PoweredByFooter/index.js.map +1 -1
  6. package/build/src/components/DaimoPay.js +4 -0
  7. package/build/src/components/DaimoPay.js.map +1 -1
  8. package/build/src/components/DaimoPayButton/index.js +7 -0
  9. package/build/src/components/DaimoPayButton/index.js.map +1 -1
  10. package/build/src/components/DaimoPayModal/ConnectWithInjector/index.js +2 -2
  11. package/build/src/components/Pages/PayWithToken/index.js +2 -2
  12. package/build/src/components/Pages/SelectMethod/index.js +40 -26
  13. package/build/src/components/Pages/SelectMethod/index.js.map +1 -1
  14. package/build/src/components/Pages/WaitingExternal/index.js +1 -1
  15. package/build/src/hooks/useExternalPaymentOptions.js +1 -0
  16. package/build/src/hooks/useExternalPaymentOptions.js.map +1 -1
  17. package/build/src/hooks/usePayWithSolanaToken.js +1 -2
  18. package/build/src/hooks/usePayWithSolanaToken.js.map +1 -1
  19. package/build/src/hooks/usePayWithToken.js +15 -13
  20. package/build/src/hooks/usePayWithToken.js.map +1 -1
  21. package/build/src/hooks/usePaymentState.js +5 -3
  22. package/build/src/hooks/usePaymentState.js.map +1 -1
  23. package/build/src/hooks/useWalletPaymentOptions.js +2 -1
  24. package/build/src/hooks/useWalletPaymentOptions.js.map +1 -1
  25. package/build/src/utils/supportUrl.js +10 -11
  26. package/build/src/utils/supportUrl.js.map +1 -1
  27. package/package.json +3 -3
package/build/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import React$1, { ReactNode } from 'react';
2
2
  export { version } from '../package.json';
3
3
  import * as react_jsx_runtime from 'react/jsx-runtime';
4
- import { DaimoPayOrderMode, DepositAddressPaymentOptionMetadata, PlatformType, ExternalPaymentOptionMetadata, WalletPaymentOption, DaimoPayOrder, DaimoPayTokenAmount, ExternalPaymentOptions, DepositAddressPaymentOptions, DepositAddressPaymentOptionData, SolanaPublicKey, DaimoPayUserMetadata, PaymentStartedEvent, PaymentCompletedEvent, PaymentBouncedEvent, DaimoPayIntentStatus } from '@daimo/common';
4
+ import { DaimoPayOrderMode, DepositAddressPaymentOptionMetadata, PlatformType, ExternalPaymentOptionMetadata, WalletPaymentOption, DaimoPayOrder, ExternalPaymentOptions, DepositAddressPaymentOptions, DepositAddressPaymentOptionData, SolanaPublicKey, DaimoPayUserMetadata, PaymentStartedEvent, PaymentCompletedEvent, PaymentBouncedEvent, DaimoPayIntentStatus, DaimoPayOrderView } from '@daimo/common';
5
5
  import { Address, Hex } from 'viem';
6
6
  import { AppRouter } from '@daimo/pay-api';
7
7
  import { CreateTRPCClient } from '@trpc/client';
@@ -70,7 +70,7 @@ type DaimoPayModalOptions = {
70
70
  closeOnSuccess?: boolean;
71
71
  };
72
72
  /** Additional payment options. Onchain payments are always enabled. */
73
- type PaymentOption = "Daimo" | "Coinbase" | "Binance" | "RampNetwork";
73
+ type PaymentOption = "Daimo" | "Coinbase" | "Binance" | "RampNetwork" | "Solana" | "ExternalChains";
74
74
 
75
75
  type types_d_All = All;
76
76
  type types_d_CustomAvatarProps = CustomAvatarProps;
@@ -127,7 +127,7 @@ declare function useSolanaPaymentOptions({ trpc, address, usdRequired, isDeposit
127
127
  };
128
128
 
129
129
  /** Wallet payment options. User picks one. */
130
- declare function useWalletPaymentOptions({ trpc, address, usdRequired, destChainId, preferredChains, preferredTokens, isDepositFlow, log, }: {
130
+ declare function useWalletPaymentOptions({ trpc, address, usdRequired, destChainId, preferredChains, preferredTokens, evmChains, isDepositFlow, log, }: {
131
131
  trpc: TrpcClient;
132
132
  address: string | undefined;
133
133
  usdRequired: number | undefined;
@@ -137,6 +137,7 @@ declare function useWalletPaymentOptions({ trpc, address, usdRequired, destChain
137
137
  chain: number;
138
138
  address: string;
139
139
  }[] | undefined;
140
+ evmChains: number[] | undefined;
140
141
  isDepositFlow: boolean;
141
142
  log: (msg: string) => void;
142
143
  }): {
@@ -172,6 +173,8 @@ interface PayParams {
172
173
  chain: number;
173
174
  address: Address;
174
175
  }[];
176
+ /** Only allow payments on these EVM chains. */
177
+ evmChains?: number[];
175
178
  /** External ID. E.g. a correlation ID. */
176
179
  externalId?: string;
177
180
  /** Developer metadata. E.g. correlation ID. */
@@ -203,7 +206,7 @@ interface PaymentState {
203
206
  setSelectedSolanaTokenOption: (option: WalletPaymentOption | undefined) => void;
204
207
  setSelectedDepositAddressOption: (option: DepositAddressPaymentOptionMetadata | undefined) => void;
205
208
  setChosenUsd: (usd: number) => void;
206
- payWithToken: (tokenAmount: DaimoPayTokenAmount) => Promise<void>;
209
+ payWithToken: (walletOption: WalletPaymentOption) => Promise<void>;
207
210
  payWithExternal: (option: ExternalPaymentOptions) => Promise<string>;
208
211
  payWithDepositAddress: (option: DepositAddressPaymentOptions) => Promise<DepositAddressPaymentOptionData | null>;
209
212
  payWithSolanaToken: (inputToken: SolanaPublicKey) => Promise<string | undefined>;
@@ -284,6 +287,9 @@ type ContextValue = {
284
287
  /** Custom message to display on confirmation page. */
285
288
  confirmationMessage?: string;
286
289
  setConfirmationMessage: React$1.Dispatch<React$1.SetStateAction<string | undefined>>;
290
+ /** Redirect URL to return to the app. E.g. after Coinbase, Binance, RampNetwork. */
291
+ redirectReturnUrl?: string;
292
+ setRedirectReturnUrl: React$1.Dispatch<React$1.SetStateAction<string | undefined>>;
287
293
  } & useConnectCallbackProps;
288
294
  /** Meant for internal use. This will be non-exported in a future SDK version. */
289
295
  declare const Context: React$1.Context<ContextValue | null>;
@@ -372,6 +378,10 @@ type PayButtonPaymentProps = {
372
378
  chain: number;
373
379
  address: Address;
374
380
  }[];
381
+ /**
382
+ * Only allow payments on these EVM chains.
383
+ */
384
+ evmChains?: number[];
375
385
  /**
376
386
  * External ID. E.g. a correlation ID.
377
387
  */
@@ -397,6 +407,8 @@ type PayButtonCommonProps = PayButtonPaymentProps & {
397
407
  defaultOpen?: boolean;
398
408
  /** Custom message to display on confirmation page. */
399
409
  confirmationMessage?: string;
410
+ /** Redirect URL to return to the app. E.g. after Coinbase, Binance, RampNetwork. */
411
+ redirectReturnUrl?: string;
400
412
  };
401
413
  type DaimoPayButtonProps = PayButtonCommonProps & {
402
414
  /** Light mode, dark mode, or auto. */
@@ -461,4 +473,7 @@ declare const daimoPayVersion: string;
461
473
  /** Chain ids supported by Daimo Pay. */
462
474
  declare const supportedChainIds: Set<number>;
463
475
 
464
- export { Avatar, Chain as ChainIcon, DaimoPayButton, Context as DaimoPayContext, DaimoPayProvider, types_d as Types, daimoPayVersion, defaultConfig as getDefaultConfig, supportedChainIds, useDaimoPayStatus, usePayContext, wallets };
476
+ type DaimoPayment = DaimoPayOrderView;
477
+ type DaimoPayEvent = PaymentStartedEvent | PaymentCompletedEvent | PaymentBouncedEvent;
478
+
479
+ export { Avatar, Chain as ChainIcon, DaimoPayButton, type DaimoPayButtonCustomProps, type DaimoPayButtonProps, Context as DaimoPayContext, type DaimoPayEvent, DaimoPayProvider, type DaimoPayment, types_d as Types, daimoPayVersion, defaultConfig as getDefaultConfig, supportedChainIds, useDaimoPayStatus, usePayContext, wallets };
@@ -1,5 +1,5 @@
1
1
  var name = "@daimo/pay";
2
- var version = "1.3.3";
2
+ var version = "1.4.0";
3
3
  var author = "Daimo";
4
4
  var homepage = "https://pay.daimo.com";
5
5
  var license = "BSD-2-Clause license";
@@ -38,8 +38,8 @@ var keywords = [
38
38
  "crypto"
39
39
  ];
40
40
  var dependencies = {
41
- "@daimo/common": "1.3.1",
42
- "@daimo/contract": "1.3.1",
41
+ "@daimo/common": "1.3.4",
42
+ "@daimo/contract": "1.3.4",
43
43
  "@solana/wallet-adapter-base": "^0.9.23",
44
44
  "@solana/wallet-adapter-react": "^0.15.35",
45
45
  "@solana/web3.js": "^1.95.4",
@@ -4,9 +4,9 @@ import styled from '../../../styles/styled/index.js';
4
4
  import { ModalBody } from '../Modal/styles.js';
5
5
 
6
6
  const PaymentBreakdown = ({ paymentOption }) => {
7
- const totalUsd = paymentOption.required.usd;
7
+ const subtotalUsd = paymentOption.required.usd;
8
8
  const feesUsd = paymentOption.fees.usd;
9
- const subtotalUsd = totalUsd - feesUsd;
9
+ const totalUsd = subtotalUsd + feesUsd;
10
10
  return (jsxs(FeesContainer, { children: [feesUsd > 0 && (jsxs(FeeRow, { children: [jsx(ModalBody, { children: "Subtotal" }), jsxs(ModalBody, { children: ["$", subtotalUsd.toFixed(2)] })] })), jsxs(FeeRow, { children: [jsx(ModalBody, { children: "Fees" }), feesUsd === 0 ? (jsx(Badge, { children: "Free" })) : (jsxs(ModalBody, { children: ["$", feesUsd.toFixed(2)] }))] }), jsxs(FeeRow, { style: { marginTop: 8 }, children: [jsx(ModalBody, { style: { fontWeight: 600 }, children: "Total" }), jsxs(ModalBody, { style: { fontWeight: 600 }, children: ["$", totalUsd.toFixed(2)] })] })] }));
11
11
  };
12
12
  const FeesContainer = styled.div `
@@ -4,6 +4,7 @@ import { useState, useEffect } from 'react';
4
4
  import { keyframes } from 'styled-components';
5
5
  import CrepeIcon from '../../../assets/crepe.js';
6
6
  import styled from '../../../styles/styled/index.js';
7
+ import { daimoPayVersion } from '../../../utils/exports.js';
7
8
 
8
9
  const PoweredByFooter = ({ supportUrl } = {}) => {
9
10
  const [supportVisible, setSupportVisible] = useState(false);
@@ -17,7 +18,9 @@ const PoweredByFooter = ({ supportUrl } = {}) => {
17
18
  return () => clearTimeout(timer);
18
19
  }, []);
19
20
  return (jsx(Container, { children: jsxs(TextButton, { onClick: () => {
20
- window.open(supportVisible ? supportUrl : "https://pay.daimo.com?ref=paykit", "_blank");
21
+ window.open(supportVisible
22
+ ? supportUrl
23
+ : `https://pay.daimo.com?ref=paykit-v${daimoPayVersion}`, "_blank");
21
24
  }, className: supportVisible ? "support" : "", children: [!supportVisible && jsx(CrepeIcon, {}), jsx("span", { children: supportVisible ? (jsxs(Fragment, { children: ["Need help? ", jsx(Underline, { children: "Contact support" })] })) : (jsx(Fragment, { children: "Powered by Daimo Pay" })) })] }) }));
22
25
  };
23
26
  const Container = styled(motion.div) `
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -129,6 +129,7 @@ const DaimoPayProviderWithoutSolana = ({ children, theme = "auto", mode = "auto"
129
129
  };
130
130
  const [errorMessage, setErrorMessage] = useState("");
131
131
  const [confirmationMessage, setConfirmationMessage] = useState(undefined);
132
+ const [redirectReturnUrl, setRedirectReturnUrl] = useState(undefined);
132
133
  const [resize, onResize] = useState(0);
133
134
  // Include Google Font that is needed for a themes
134
135
  if (opts.embedGoogleFonts)
@@ -170,6 +171,7 @@ const DaimoPayProviderWithoutSolana = ({ children, theme = "auto", mode = "auto"
170
171
  setDaimoPayOrder,
171
172
  setOpen,
172
173
  log,
174
+ redirectReturnUrl,
173
175
  });
174
176
  // When a payment is in progress, poll for status updates. Do this regardless
175
177
  // of whether the modal is still being displayed for useDaimoPayStatus().
@@ -235,6 +237,8 @@ const DaimoPayProviderWithoutSolana = ({ children, theme = "auto", mode = "auto"
235
237
  errorMessage,
236
238
  confirmationMessage,
237
239
  setConfirmationMessage,
240
+ redirectReturnUrl,
241
+ setRedirectReturnUrl,
238
242
  debugMode,
239
243
  log,
240
244
  displayError: (message, code) => {
@@ -1 +1 @@
1
- {"version":3,"file":"DaimoPay.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"DaimoPay.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -34,6 +34,7 @@ function DaimoPayButtonCustom(props) {
34
34
  paymentOptions: props.paymentOptions,
35
35
  preferredChains: props.preferredChains,
36
36
  preferredTokens: props.preferredTokens,
37
+ evmChains: props.evmChains,
37
38
  externalId: props.externalId,
38
39
  metadata: props.metadata,
39
40
  }
@@ -54,6 +55,12 @@ function DaimoPayButtonCustom(props) {
54
55
  setConfirmationMessage(props.confirmationMessage);
55
56
  }
56
57
  }, [props.confirmationMessage, setConfirmationMessage]);
58
+ const { setRedirectReturnUrl } = context;
59
+ useEffect(() => {
60
+ if (props.redirectReturnUrl) {
61
+ setRedirectReturnUrl(props.redirectReturnUrl);
62
+ }
63
+ }, [props.redirectReturnUrl, setRedirectReturnUrl]);
57
64
  // Payment events: call these three event handlers.
58
65
  const { onPaymentStarted, onPaymentCompleted, onPaymentBounced } = props;
59
66
  const order = paymentState.daimoPayOrder;
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -7,7 +7,7 @@ import Button from '../../Common/Button/index.js';
7
7
  import { PageContent, ModalHeading, ModalContent, ModalContentContainer, ModalH1, ModalBody } from '../../Common/Modal/styles.js';
8
8
  import Tooltip from '../../Common/Tooltip/index.js';
9
9
  import SquircleSpinner from '../../Spinners/SquircleSpinner/index.js';
10
- import { RetryIconCircle, AlertIcon, TickIcon } from '../../../assets/icons.js';
10
+ import { RetryIconCircle, AlertIcon, ExternalLinkIcon, TickIcon } from '../../../assets/icons.js';
11
11
  import { useConnect } from '../../../hooks/useConnect.js';
12
12
  import useLocales from '../../../hooks/useLocales.js';
13
13
  import { detectBrowser, isWalletConnectConnector } from '../../../utils/index.js';
@@ -195,7 +195,7 @@ const ConnectWithInjector = ({ switchConnectMethod, forceState }) => {
195
195
  ? locales.injectionScreen_connecting_injected_h1
196
196
  : locales.injectionScreen_connecting_h1 }), jsx(ModalBody, { children: wallet.connector.id === "injected"
197
197
  ? locales.injectionScreen_connecting_injected_p
198
- : locales.injectionScreen_connecting_p })] }) }, states.CONNECTING)), status === states.CONNECTED && (jsx(Content, { initial: "initial", animate: "animate", exit: "exit", variants: contentVariants, children: jsxs(ModalContent, { children: [jsxs(ModalH1, { "$valid": true, children: [jsx(TickIcon, {}), " ", locales.injectionScreen_connected_h1] }), jsx(ModalBody, { children: locales.injectionScreen_connected_p })] }) }, states.CONNECTED)), status === states.NOTCONNECTED && (jsx(Content, { initial: "initial", animate: "animate", exit: "exit", variants: contentVariants, children: jsxs(ModalContent, { children: [jsx(ModalH1, { children: locales.injectionScreen_notconnected_h1 }), jsx(ModalBody, { children: locales.injectionScreen_notconnected_p })] }) }, states.NOTCONNECTED)), status === states.UNAVAILABLE && (jsx(Content, { initial: "initial", animate: "animate", exit: "exit", variants: contentVariants, children: !extensionUrl ? (jsxs(Fragment, { children: [jsxs(ModalContent, { style: { paddingBottom: 12 }, children: [jsx(ModalH1, { children: locales.injectionScreen_unavailable_h1 }), jsx(ModalBody, { children: locales.injectionScreen_unavailable_p })] }), !wallet.isInstalled && suggestedExtension && (jsxs(Button, { href: suggestedExtension?.url, icon: jsx(BrowserIcon, { browser: suggestedExtension?.name }), children: ["Install on ", suggestedExtension?.label] }))] })) : (jsxs(Fragment, { children: [jsxs(ModalContent, { style: { paddingBottom: 18 }, children: [jsx(ModalH1, { children: locales.injectionScreen_install_h1 }), jsx(ModalBody, { children: locales.injectionScreen_install_p })] }), !wallet.isInstalled && extensionUrl && (jsx(Button, { href: extensionUrl, icon: jsx(BrowserIcon, {}), children: locales.installTheExtension }))] })) }, states.UNAVAILABLE))] }) })] }) }));
198
+ : locales.injectionScreen_connecting_p }), jsxs(Button, { icon: jsx(ExternalLinkIcon, {}), onClick: runConnect, children: ["Connect ", walletInfo.name] })] }) }, states.CONNECTING)), status === states.CONNECTED && (jsx(Content, { initial: "initial", animate: "animate", exit: "exit", variants: contentVariants, children: jsxs(ModalContent, { children: [jsxs(ModalH1, { "$valid": true, children: [jsx(TickIcon, {}), " ", locales.injectionScreen_connected_h1] }), jsx(ModalBody, { children: locales.injectionScreen_connected_p })] }) }, states.CONNECTED)), status === states.NOTCONNECTED && (jsx(Content, { initial: "initial", animate: "animate", exit: "exit", variants: contentVariants, children: jsxs(ModalContent, { children: [jsx(ModalH1, { children: locales.injectionScreen_notconnected_h1 }), jsx(ModalBody, { children: locales.injectionScreen_notconnected_p })] }) }, states.NOTCONNECTED)), status === states.UNAVAILABLE && (jsx(Content, { initial: "initial", animate: "animate", exit: "exit", variants: contentVariants, children: !extensionUrl ? (jsxs(Fragment, { children: [jsxs(ModalContent, { style: { paddingBottom: 12 }, children: [jsx(ModalH1, { children: locales.injectionScreen_unavailable_h1 }), jsx(ModalBody, { children: locales.injectionScreen_unavailable_p })] }), !wallet.isInstalled && suggestedExtension && (jsxs(Button, { href: suggestedExtension?.url, icon: jsx(BrowserIcon, { browser: suggestedExtension?.name }), children: ["Install on ", suggestedExtension?.label] }))] })) : (jsxs(Fragment, { children: [jsxs(ModalContent, { style: { paddingBottom: 18 }, children: [jsx(ModalH1, { children: locales.injectionScreen_install_h1 }), jsx(ModalBody, { children: locales.injectionScreen_install_p })] }), !wallet.isInstalled && extensionUrl && (jsx(Button, { href: extensionUrl, icon: jsx(BrowserIcon, {}), children: locales.installTheExtension }))] })) }, states.UNAVAILABLE))] }) })] }) }));
199
199
  };
200
200
 
201
201
  export { ConnectWithInjector as default, states };
@@ -50,7 +50,7 @@ const PayWithToken = () => {
50
50
  }
51
51
  setPayState(PayState.RequestingPayment);
52
52
  try {
53
- await payWithToken(option.required);
53
+ await payWithToken(option);
54
54
  setPayState(PayState.RequestSuccessful);
55
55
  setTimeout(() => {
56
56
  setRoute(ROUTES.CONFIRMATION, { event: "wait-pay-with-token" });
@@ -64,7 +64,7 @@ const PayWithToken = () => {
64
64
  const switchSuccessful = await trySwitchingChain(option, true);
65
65
  if (switchSuccessful) {
66
66
  try {
67
- await payWithToken(option.required);
67
+ await payWithToken(option);
68
68
  return; // Payment successful after switching chain
69
69
  }
70
70
  catch (retryError) {
@@ -1,7 +1,7 @@
1
1
  import { jsx, jsxs } from 'react/jsx-runtime';
2
2
  import { usePayContext, ROUTES } from '../../DaimoPay.js';
3
3
  import { PageContent } from '../../Common/Modal/styles.js';
4
- import { getAddressContraction } from '@daimo/common';
4
+ import { getAddressContraction, ExternalPaymentOptions } from '@daimo/common';
5
5
  import { useWallet } from '@solana/wallet-adapter-react';
6
6
  import { useAccount, useDisconnect } from 'wagmi';
7
7
  import { Solana, Bitcoin, Tron, Zcash } from '../../../assets/chains.js';
@@ -67,8 +67,9 @@ const SelectMethod = () => {
67
67
  const { address, chain, isConnected, connector } = useAccount();
68
68
  const { disconnectAsync } = useDisconnect();
69
69
  const { setRoute, paymentState, log } = usePayContext();
70
- const { setSelectedExternalOption, externalPaymentOptions, depositAddressOptions, senderEnsName, } = paymentState;
70
+ const { daimoPayOrder, setSelectedExternalOption, externalPaymentOptions, depositAddressOptions, senderEnsName, } = paymentState;
71
71
  const displayName = senderEnsName ?? (address ? getAddressContraction(address) : "wallet");
72
+ const paymentOptions = daimoPayOrder?.metadata.payer?.paymentOptions;
72
73
  const connectedWalletOption = isConnected
73
74
  ? {
74
75
  id: "connectedWallet",
@@ -97,30 +98,43 @@ const SelectMethod = () => {
97
98
  ? [connectedWalletOption, unconnectedWalletOption]
98
99
  : [unconnectedWalletOption];
99
100
  log(`[SELECT_METHOD] loading: ${externalPaymentOptions.loading}, options: ${JSON.stringify(externalPaymentOptions.options)}`);
100
- const solanaOption = getSolanaOption();
101
- const depositAddressOption = getDepositAddressOption(depositAddressOptions);
102
- const options = [
103
- ...walletOptions,
104
- ...(solanaOption ? [solanaOption] : []),
105
- ...(externalPaymentOptions.options ?? []).map((option) => ({
106
- id: option.id,
107
- title: option.cta,
108
- icons: [option.logoURI],
109
- onClick: () => {
110
- setSelectedExternalOption(option);
111
- const meta = { event: "click-option", option: option.id };
112
- if (paymentState.isDepositFlow) {
113
- setRoute(ROUTES.SELECT_EXTERNAL_AMOUNT, meta);
114
- }
115
- else {
116
- setRoute(ROUTES.WAITING_EXTERNAL, meta);
117
- }
118
- },
119
- disabled: option.disabled,
120
- subtitle: option.message,
121
- })),
122
- ...(depositAddressOption ? [depositAddressOption] : []),
123
- ];
101
+ const options = [...walletOptions];
102
+ // Solana payment option
103
+ // Include by default if paymentOptions not provided
104
+ const includeSolana = paymentOptions == null ||
105
+ paymentOptions.includes(ExternalPaymentOptions.Solana);
106
+ if (includeSolana) {
107
+ const solanaOption = getSolanaOption();
108
+ if (solanaOption) {
109
+ options.push(solanaOption);
110
+ }
111
+ }
112
+ // External payment options, e.g. Binance, Coinbase, etc.
113
+ options.push(...(externalPaymentOptions.options ?? []).map((option) => ({
114
+ id: option.id,
115
+ title: option.cta,
116
+ icons: [option.logoURI],
117
+ onClick: () => {
118
+ setSelectedExternalOption(option);
119
+ const meta = { event: "click-option", option: option.id };
120
+ if (paymentState.isDepositFlow) {
121
+ setRoute(ROUTES.SELECT_EXTERNAL_AMOUNT, meta);
122
+ }
123
+ else {
124
+ setRoute(ROUTES.WAITING_EXTERNAL, meta);
125
+ }
126
+ },
127
+ disabled: option.disabled,
128
+ subtitle: option.message,
129
+ })));
130
+ // Deposit address options, e.g. Bitcoin, Tron, Zcash, etc.
131
+ // Include by default if paymentOptions not provided
132
+ const includeDepositAddressOption = paymentOptions == null ||
133
+ paymentOptions.includes(ExternalPaymentOptions.ExternalChains);
134
+ if (includeDepositAddressOption) {
135
+ const depositAddressOption = getDepositAddressOption(depositAddressOptions);
136
+ options.push(depositAddressOption);
137
+ }
124
138
  return (jsxs(PageContent, { children: [jsx(OrderHeader, {}), jsx(OptionsList, { requiredSkeletons: isMobile ? 4 : 3, isLoading: externalPaymentOptions.loading, options: externalPaymentOptions.loading ? [] : options }), jsx(PoweredByFooter, {})] }));
125
139
  };
126
140
 
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -46,7 +46,7 @@ const WaitingExternal = () => {
46
46
  return (jsxs(PageContent, { children: [jsx(ExternalPaymentSpinner, { logoURI: selectedExternalOption.logoURI, logoShape: selectedExternalOption.logoShape }), jsxs(ModalContent, { style: { marginLeft: 24, marginRight: 24 }, children: [jsx(ModalH1, { children: "Waiting for Payment" }), paymentWaitingMessage && (jsx(ModalBody, { style: { marginTop: 12, marginBottom: 12 }, children: paymentWaitingMessage }))] }), jsx(Button, { icon: jsx(ExternalLinkIcon, {}), onClick: () => {
47
47
  if (externalURL)
48
48
  window.open(externalURL, "_blank");
49
- }, children: "Open in New Tab" })] }));
49
+ }, children: selectedExternalOption.cta })] }));
50
50
  };
51
51
 
52
52
  export { WaitingExternal as default };
@@ -6,6 +6,7 @@ const DEFAULT_EXTERNAL_PAYMENT_OPTIONS = [
6
6
  ExternalPaymentOptions.Binance,
7
7
  ExternalPaymentOptions.Daimo,
8
8
  ExternalPaymentOptions.RampNetwork,
9
+ // Solana and ExternalChains are handled in the SelectMethod component.
9
10
  ];
10
11
  function useExternalPaymentOptions({ trpc, filterIds, platform, usdRequired, mode, }) {
11
12
  const [options, setOptions] = useState([]);
@@ -1 +1 @@
1
- {"version":3,"file":"useExternalPaymentOptions.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"useExternalPaymentOptions.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -3,13 +3,12 @@ import { useConnection, useWallet } from '@solana/wallet-adapter-react';
3
3
  import { VersionedTransaction } from '@solana/web3.js';
4
4
  import { hexToBytes } from 'viem';
5
5
 
6
- function usePayWithSolanaToken({ trpc, daimoPayOrder, setDaimoPayOrder, createOrHydrate, platform, log, }) {
6
+ function usePayWithSolanaToken({ trpc, daimoPayOrder, setDaimoPayOrder, createOrHydrate, log, }) {
7
7
  const { connection } = useConnection();
8
8
  const wallet = useWallet();
9
9
  const payWithSolanaToken = async (inputToken) => {
10
10
  assert(!!wallet.publicKey, "[PAY SOLANA] No wallet connected");
11
11
  assert(!!daimoPayOrder, "[PAY SOLANA] daimoPayOrder cannot be null");
12
- assert(!!platform, "[PAY SOLANA] platform cannot be null");
13
12
  const orderId = daimoPayOrder.id;
14
13
  const { hydratedOrder } = await createOrHydrate({
15
14
  order: daimoPayOrder,
@@ -1 +1 @@
1
- {"version":3,"file":"usePayWithSolanaToken.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"usePayWithSolanaToken.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -1,39 +1,41 @@
1
- import { assert, assertNotNull } from '@daimo/common';
1
+ import { assert, debugJson, assertNotNull } from '@daimo/common';
2
2
  import { zeroAddress, erc20Abi, getAddress } from 'viem';
3
3
  import { useWriteContract, useSendTransaction } from 'wagmi';
4
4
 
5
- function usePayWithToken({ trpc, senderAddr, daimoPayOrder, setDaimoPayOrder, createOrHydrate, platform, log, }) {
5
+ function usePayWithToken({ trpc, senderAddr, daimoPayOrder, setDaimoPayOrder, createOrHydrate, log, }) {
6
6
  const { writeContractAsync } = useWriteContract();
7
7
  const { sendTransactionAsync } = useSendTransaction();
8
8
  /** Commit to a token + amount = initiate payment. */
9
- const payWithToken = async (tokenAmount) => {
9
+ const payWithToken = async (walletOption) => {
10
10
  assert(!!daimoPayOrder, "[PAY TOKEN] daimoPayOrder cannot be null");
11
- assert(!!platform, "[PAY TOKEN] platform cannot be null");
11
+ const { required, fees } = walletOption;
12
+ assert(required.token.token === fees.token.token, `[PAY TOKEN] required token ${debugJson(required)} does not match fees token ${debugJson(fees)}`);
13
+ const paymentAmount = BigInt(required.amount) + BigInt(fees.amount);
12
14
  const { hydratedOrder } = await createOrHydrate({
13
15
  order: daimoPayOrder,
14
16
  refundAddress: senderAddr,
15
17
  });
16
- log(`[CHECKOUT] hydrated order: ${JSON.stringify(hydratedOrder)}, checking out with ${tokenAmount.token.token}`);
18
+ log(`[PAY TOKEN] hydrated order: ${JSON.stringify(hydratedOrder)}, paying ${paymentAmount} of token ${required.token.token}`);
17
19
  setDaimoPayOrder(hydratedOrder);
18
20
  const txHash = await (async () => {
19
21
  try {
20
- if (tokenAmount.token.token === zeroAddress) {
22
+ if (required.token.token === zeroAddress) {
21
23
  return await sendTransactionAsync({
22
24
  to: hydratedOrder.intentAddr,
23
- value: BigInt(tokenAmount.amount),
25
+ value: paymentAmount,
24
26
  });
25
27
  }
26
28
  else {
27
29
  return await writeContractAsync({
28
30
  abi: erc20Abi,
29
- address: getAddress(tokenAmount.token.token),
31
+ address: getAddress(required.token.token),
30
32
  functionName: "transfer",
31
- args: [hydratedOrder.intentAddr, BigInt(tokenAmount.amount)],
33
+ args: [hydratedOrder.intentAddr, paymentAmount],
32
34
  });
33
35
  }
34
36
  }
35
37
  catch (e) {
36
- console.error(`[CHECKOUT] error sending token: ${e}`);
38
+ console.error(`[PAY TOKEN] error sending token: ${e}`);
37
39
  throw e;
38
40
  }
39
41
  })();
@@ -41,10 +43,10 @@ function usePayWithToken({ trpc, senderAddr, daimoPayOrder, setDaimoPayOrder, cr
41
43
  await trpc.processSourcePayment.mutate({
42
44
  orderId: daimoPayOrder.id.toString(),
43
45
  sourceInitiateTxHash: txHash,
44
- sourceChainId: tokenAmount.token.chainId,
46
+ sourceChainId: required.token.chainId,
45
47
  sourceFulfillerAddr: assertNotNull(senderAddr, `[PAY TOKEN] senderAddr cannot be null on order ${daimoPayOrder.id}`),
46
- sourceToken: tokenAmount.token.token,
47
- sourceAmount: tokenAmount.amount,
48
+ sourceToken: required.token.token,
49
+ sourceAmount: paymentAmount.toString(),
48
50
  });
49
51
  // TODO: update order immediately, do not wait for polling.
50
52
  }
@@ -1 +1 @@
1
- {"version":3,"file":"usePayWithToken.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"usePayWithToken.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -13,7 +13,7 @@ import { usePayWithToken } from './usePayWithToken.js';
13
13
  import { useSolanaPaymentOptions } from './useSolanaPaymentOptions.js';
14
14
  import { useWalletPaymentOptions } from './useWalletPaymentOptions.js';
15
15
 
16
- function usePaymentState({ trpc, daimoPayOrder, setDaimoPayOrder, setOpen, log, }) {
16
+ function usePaymentState({ trpc, daimoPayOrder, setDaimoPayOrder, setOpen, log, redirectReturnUrl, }) {
17
17
  // Browser state.
18
18
  const [platform, setPlatform] = useState();
19
19
  useEffect(() => {
@@ -49,6 +49,7 @@ function usePaymentState({ trpc, daimoPayOrder, setDaimoPayOrder, setOpen, log,
49
49
  destChainId: daimoPayOrder?.destFinalCallTokenAmount.token.chainId,
50
50
  preferredChains: daimoPayOrder?.metadata.payer?.preferredChains,
51
51
  preferredTokens: daimoPayOrder?.metadata.payer?.preferredTokens,
52
+ evmChains: daimoPayOrder?.metadata.payer?.evmChains,
52
53
  isDepositFlow,
53
54
  log,
54
55
  });
@@ -75,6 +76,7 @@ function usePaymentState({ trpc, daimoPayOrder, setDaimoPayOrder, setOpen, log,
75
76
  platform,
76
77
  refundAddress,
77
78
  externalPaymentOption,
79
+ redirectReturnUrl,
78
80
  });
79
81
  }
80
82
  log(`[CREATE/HYDRATE] creating+hydrating new order ${order.id}`);
@@ -93,6 +95,7 @@ function usePaymentState({ trpc, daimoPayOrder, setDaimoPayOrder, setOpen, log,
93
95
  platform,
94
96
  refundAddress,
95
97
  externalPaymentOption,
98
+ redirectReturnUrl,
96
99
  });
97
100
  };
98
101
  const { payWithToken } = usePayWithToken({
@@ -101,7 +104,6 @@ function usePaymentState({ trpc, daimoPayOrder, setDaimoPayOrder, setOpen, log,
101
104
  daimoPayOrder,
102
105
  setDaimoPayOrder,
103
106
  createOrHydrate,
104
- platform,
105
107
  log,
106
108
  });
107
109
  const { payWithSolanaToken } = usePayWithSolanaToken({
@@ -109,7 +111,6 @@ function usePaymentState({ trpc, daimoPayOrder, setDaimoPayOrder, setOpen, log,
109
111
  daimoPayOrder,
110
112
  setDaimoPayOrder,
111
113
  createOrHydrate,
112
- platform,
113
114
  log,
114
115
  });
115
116
  const [selectedExternalOption, setSelectedExternalOption] = useState();
@@ -229,6 +230,7 @@ function usePaymentState({ trpc, daimoPayOrder, setDaimoPayOrder, setOpen, log,
229
230
  paymentOptions: payParams.paymentOptions,
230
231
  preferredChains: payParams.preferredChains,
231
232
  preferredTokens: payParams.preferredTokens,
233
+ evmChains: payParams.evmChains,
232
234
  },
233
235
  },
234
236
  externalId: payParams.externalId,
@@ -1 +1 @@
1
- {"version":3,"file":"usePaymentState.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"usePaymentState.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -2,7 +2,7 @@ import { useState, useEffect } from 'react';
2
2
  import { supportedChainIds } from '../utils/exports.js';
3
3
 
4
4
  /** Wallet payment options. User picks one. */
5
- function useWalletPaymentOptions({ trpc, address, usdRequired, destChainId, preferredChains, preferredTokens, isDepositFlow, log, }) {
5
+ function useWalletPaymentOptions({ trpc, address, usdRequired, destChainId, preferredChains, preferredTokens, evmChains, isDepositFlow, log, }) {
6
6
  const [options, setOptions] = useState(null);
7
7
  const [isLoading, setIsLoading] = useState(false);
8
8
  useEffect(() => {
@@ -19,6 +19,7 @@ function useWalletPaymentOptions({ trpc, address, usdRequired, destChainId, pref
19
19
  destChainId,
20
20
  preferredChains,
21
21
  preferredTokens,
22
+ evmChains,
22
23
  });
23
24
  // Filter out chains we don't support yet.
24
25
  const isSupported = (o) => supportedChainIds.has(o.balance.token.chainId);
@@ -1 +1 @@
1
- {"version":3,"file":"useWalletPaymentOptions.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"useWalletPaymentOptions.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -1,18 +1,17 @@
1
1
  import { writeDaimoPayOrderID } from '@daimo/common';
2
+ import { daimoPayVersion } from './exports.js';
2
3
 
3
4
  function getSupportUrl(daimoPayOrder, screen) {
4
- const encodedOrderId = daimoPayOrder == null ? null : writeDaimoPayOrderID(daimoPayOrder.id);
5
+ const payId = daimoPayOrder == null ? null : writeDaimoPayOrderID(daimoPayOrder.id);
5
6
  const email = "support@daimo.com";
6
- const subject = `Support with Daimo Pay Id ${encodedOrderId}`;
7
- let body = "";
8
- if (daimoPayOrder != null) {
9
- body += `Id: ${encodedOrderId}\n`;
10
- body += `Org Id: ${daimoPayOrder.orgId}\n`;
11
- }
12
- body += `Support requested on ${screen} screen at ${new Date().toISOString()}\n\n`;
13
- body += "Please explain the issue you are experiencing:\n\n";
14
- const mailtoUrl = `mailto:${email}?subject=${encodeURIComponent(subject)}&body=${encodeURIComponent(body)}`;
15
- return mailtoUrl;
7
+ const subject = `Support${payId ? ` #${payId}` : ""}`;
8
+ let body = [
9
+ `Transaction: ${screen}`,
10
+ `Version: ${daimoPayVersion}`,
11
+ ``,
12
+ `Tell us how we can help`,
13
+ ].join("\n");
14
+ return `mailto:${email}?subject=${encodeURIComponent(subject)}&body=${encodeURIComponent(body)}`;
16
15
  }
17
16
 
18
17
  export { getSupportUrl };
@@ -1 +1 @@
1
- {"version":3,"file":"supportUrl.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"supportUrl.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.3.3",
4
+ "version": "1.4.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": "1.3.1",
44
- "@daimo/contract": "1.3.1",
43
+ "@daimo/common": "1.3.4",
44
+ "@daimo/contract": "1.3.4",
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",