@daimo/pay 1.1.5 → 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.
- package/build/index.d.ts +37 -105
- package/build/package.json.js +3 -4
- package/build/package.json.js.map +1 -1
- package/build/src/assets/icons.js +2 -1
- package/build/src/assets/icons.js.map +1 -1
- package/build/src/components/Common/AmountInput/AmountInputField.js +59 -0
- package/build/src/components/Common/AmountInput/AmountInputField.js.map +1 -0
- package/build/src/components/Common/AmountInput/index.js +143 -0
- package/build/src/components/Common/AmountInput/index.js.map +1 -0
- package/build/src/components/Common/ChainSelectList/index.js +1 -1
- package/build/src/components/Common/Modal/index.js +9 -4
- package/build/src/components/Common/Modal/index.js.map +1 -1
- package/build/src/components/Common/Modal/styles.js +2 -1
- package/build/src/components/Common/Modal/styles.js.map +1 -1
- package/build/src/components/Common/OptionsList/styles.js +1 -1
- package/build/src/components/Common/OrderHeader/index.js +22 -111
- package/build/src/components/Common/OrderHeader/index.js.map +1 -1
- package/build/src/components/Common/PaymentBreakdown/index.js +48 -0
- package/build/src/components/{Pages/WaitingOther → Common/PaymentBreakdown}/index.js.map +1 -1
- package/build/src/components/Common/SwitchButton/index.js +39 -0
- package/build/src/components/Common/SwitchButton/index.js.map +1 -0
- package/build/src/components/Common/TokenChainLogo/index.js +24 -0
- package/build/src/components/Common/TokenChainLogo/index.js.map +1 -0
- package/build/src/components/DaimoPay.js +7 -2
- package/build/src/components/DaimoPay.js.map +1 -1
- package/build/src/components/DaimoPayButton/index.js +0 -1
- package/build/src/components/DaimoPayButton/index.js.map +1 -1
- package/build/src/components/DaimoPayModal/index.js +86 -26
- package/build/src/components/DaimoPayModal/index.js.map +1 -1
- package/build/src/components/Pages/PayWithToken/index.js +4 -109
- package/build/src/components/Pages/PayWithToken/index.js.map +1 -1
- package/build/src/components/Pages/SelectAmount/index.js +16 -0
- package/build/src/components/Pages/SelectAmount/index.js.map +1 -0
- package/build/src/components/Pages/SelectDepositAddressAmount/index.js +60 -0
- package/build/src/components/Pages/SelectDepositAddressAmount/index.js.map +1 -0
- package/build/src/components/Pages/SelectDepositAddressChain/index.js +7 -2
- package/build/src/components/Pages/SelectDepositAddressChain/index.js.map +1 -1
- package/build/src/components/Pages/SelectExternalAmount/index.js +62 -0
- package/build/src/components/Pages/SelectExternalAmount/index.js.map +1 -0
- package/build/src/components/Pages/SelectMethod/index.js +14 -7
- package/build/src/components/Pages/SelectMethod/index.js.map +1 -1
- package/build/src/components/Pages/SelectToken/index.js +36 -41
- package/build/src/components/Pages/SelectToken/index.js.map +1 -1
- package/build/src/components/Pages/Solana/ConnectorSolana/index.js +8 -2
- package/build/src/components/Pages/Solana/ConnectorSolana/index.js.map +1 -1
- package/build/src/components/Pages/Solana/PayWithSolanaToken/index.js +14 -8
- package/build/src/components/Pages/Solana/PayWithSolanaToken/index.js.map +1 -1
- package/build/src/components/Pages/Solana/SelectSolanaAmount/index.js +16 -0
- package/build/src/components/Pages/Solana/SelectSolanaAmount/index.js.map +1 -0
- package/build/src/components/Pages/Solana/SelectSolanaToken/index.js +37 -21
- package/build/src/components/Pages/Solana/SelectSolanaToken/index.js.map +1 -1
- package/build/src/components/Pages/WaitingExternal/index.js +53 -0
- package/build/src/components/Pages/WaitingExternal/index.js.map +1 -0
- package/build/src/components/Spinners/ExternalPaymentSpinner/index.js +20 -0
- package/build/src/components/Spinners/ExternalPaymentSpinner/index.js.map +1 -0
- package/build/src/components/Spinners/TokenLogoSpinner/index.js +13 -0
- package/build/src/components/Spinners/TokenLogoSpinner/index.js.map +1 -0
- package/build/src/components/Spinners/TokenLogoSpinner/styles.js +40 -0
- package/build/src/components/Spinners/TokenLogoSpinner/styles.js.map +1 -0
- package/build/src/components/Spinners/styles.js +32 -0
- package/build/src/components/Spinners/styles.js.map +1 -0
- package/build/src/hooks/useDepositAddressOptions.js +8 -5
- package/build/src/hooks/useDepositAddressOptions.js.map +1 -1
- package/build/src/hooks/useExternalPaymentOptions.js +7 -6
- package/build/src/hooks/useExternalPaymentOptions.js.map +1 -1
- package/build/src/hooks/useOrderUsdLimits.js +26 -0
- package/build/src/hooks/useOrderUsdLimits.js.map +1 -0
- package/build/src/hooks/usePaymentState.js +46 -14
- package/build/src/hooks/usePaymentState.js.map +1 -1
- package/build/src/hooks/useSolanaPaymentOptions.js +7 -5
- package/build/src/hooks/useSolanaPaymentOptions.js.map +1 -1
- package/build/src/hooks/useWalletPaymentOptions.js +9 -8
- package/build/src/hooks/useWalletPaymentOptions.js.map +1 -1
- package/build/src/index.js +1 -1
- package/build/src/utils/exports.js +4 -1
- package/build/src/utils/exports.js.map +1 -1
- package/build/src/utils/format.js +83 -0
- package/build/src/utils/format.js.map +1 -0
- package/build/src/utils/validateInput.js +36 -0
- package/build/src/utils/validateInput.js.map +1 -0
- package/package.json +4 -5
- 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
|
-
|
|
23
|
-
|
|
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,
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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 = (
|
|
148
|
-
log(`[CHECKOUT] Setting chosen USD amount to ${
|
|
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((
|
|
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:
|
|
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
|
-
|
|
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
|
|
217
|
+
toUnits,
|
|
190
218
|
toAddress: payParams.toAddress,
|
|
191
219
|
toCallData: payParams.toCallData,
|
|
192
|
-
isAmountEditable: payParams.
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
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
|
-
|
|
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
|
|
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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
package/build/src/index.js
CHANGED
|
@@ -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.
|
|
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": "1.1.
|
|
44
|
-
"@daimo/contract": "1.1.
|
|
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",
|
|
@@ -80,4 +79,4 @@
|
|
|
80
79
|
"rollup-plugin-visualizer": "^5.5.4",
|
|
81
80
|
"typescript-plugin-styled-components": "^3.0.0"
|
|
82
81
|
}
|
|
83
|
-
}
|
|
82
|
+
}
|