@daimo/pay
Version:
Seamless crypto payments. Onboard users from any chain, any coin into your app with one click.
186 lines (183 loc) • 6.09 kB
JavaScript
import { ExternalPaymentOptions, supportedChains } from '@daimo/pay-common';
import { useState, useMemo, useEffect } from 'react';
import { inferTopLevelFromArray } from '../constants/paymentOptions.js';
const DEFAULT_EXTERNAL_PAYMENT_OPTIONS = Object.values(
ExternalPaymentOptions
).filter(
(opt) => opt !== ExternalPaymentOptions.AllAddresses && opt !== ExternalPaymentOptions.AllPaymentApps
);
function usePaymentOptions({
trpc,
appId,
orderId,
isDepositFlow,
usdRequired,
solanaPubKey,
ethWalletAddress,
platform,
// For filtering/preferences from order metadata
filterIds,
preferredChains,
preferredTokens,
evmChains,
destChainId,
passthroughTokens,
destAddress,
log
}) {
const [externalOptions, setExternalOptions] = useState(/* @__PURE__ */ new Map());
const [walletOptions, setWalletOptions] = useState(null);
const [solanaOptions, setSolanaOptions] = useState(null);
const [depositOptions, setDepositOptions] = useState([]);
const [parsedConfig, setParsedConfig] = useState({
walletOrder: []
});
const [loading, setLoading] = useState(false);
const memoizedPreferredChains = useMemo(
() => preferredChains,
// eslint-disable-next-line react-hooks/exhaustive-deps
[JSON.stringify(preferredChains)]
);
const memoizedPreferredTokens = useMemo(
() => preferredTokens,
// eslint-disable-next-line react-hooks/exhaustive-deps
[JSON.stringify(preferredTokens)]
);
const memoizedEvmChains = useMemo(
() => evmChains,
// eslint-disable-next-line react-hooks/exhaustive-deps
[JSON.stringify(evmChains)]
);
const memoizedFilterIds = useMemo(
() => filterIds,
// eslint-disable-next-line react-hooks/exhaustive-deps
[JSON.stringify(filterIds)]
);
useEffect(() => {
const fetchPaymentOptions = async () => {
if (!platform) return;
if (!appId && !orderId) return;
if (!isDepositFlow && !orderId && !usdRequired) return;
setLoading(true);
try {
const result = await trpc.getPaymentOptionsV1.query({
appId,
orderId: orderId?.toString(),
platform,
ethWalletAddress,
preferredChains: memoizedPreferredChains,
preferredTokens: memoizedPreferredTokens,
evmChains: memoizedEvmChains,
destChainId,
solanaPubKey,
usdRequired,
isDepositFlow
});
let walletOrder = [];
if (memoizedFilterIds) {
const nestedArray = memoizedFilterIds.find(
(opt) => Array.isArray(opt)
);
if (nestedArray && Array.isArray(nestedArray)) {
walletOrder = nestedArray;
}
}
setParsedConfig({ walletOrder });
const flatFilterIds = memoizedFilterIds ? memoizedFilterIds.flatMap(
(opt) => Array.isArray(opt) ? inferTopLevelFromArray(opt) ?? "AllWallets" : opt
) : null;
const enabledExtPaymentOptions = flatFilterIds || DEFAULT_EXTERNAL_PAYMENT_OPTIONS;
const hasAllPaymentApps = enabledExtPaymentOptions.includes(
ExternalPaymentOptions.AllPaymentApps
);
const hasAllExchanges = enabledExtPaymentOptions.includes(
ExternalPaymentOptions.AllExchanges
);
const filteredExternal = result.externalOptions.filter(
(option) => enabledExtPaymentOptions.includes(option.id) || hasAllPaymentApps && option.optionType === "zkp2p" || hasAllExchanges && option.optionType === "exchange"
);
const optionsByType = /* @__PURE__ */ new Map();
filteredExternal.forEach((option) => {
const { optionType } = option;
if (!optionsByType.has(optionType)) {
optionsByType.set(optionType, []);
}
optionsByType.get(optionType).push(option);
});
setExternalOptions(optionsByType);
if (result.walletOptions.length > 0) {
const isSupported = (o) => supportedChains.some((c) => c.chainId === o.balance.token.chainId);
const filtered = result.walletOptions.filter(isSupported);
if (filtered.length < result.walletOptions.length) {
log(
`[WALLET]: skipping ${result.walletOptions.length - filtered.length} unsupported-chain balances`
);
}
if (destAddress) {
addPassthroughTokens(filtered, passthroughTokens, destAddress);
}
setWalletOptions(filtered);
} else {
setWalletOptions(ethWalletAddress ? [] : null);
}
setSolanaOptions(solanaPubKey ? result.solanaOptions : null);
setDepositOptions(result.depositAddressOptions);
} catch (error) {
console.error("[usePaymentOptions] error:", error);
} finally {
setLoading(false);
}
};
fetchPaymentOptions();
}, [
trpc,
appId,
orderId,
isDepositFlow,
usdRequired,
solanaPubKey,
ethWalletAddress,
platform,
memoizedPreferredChains,
memoizedPreferredTokens,
memoizedEvmChains,
memoizedFilterIds,
destChainId,
destAddress,
passthroughTokens,
log
]);
return {
externalPaymentOptions: {
options: externalOptions,
loading,
parsedConfig
},
walletPaymentOptions: {
options: walletOptions,
isLoading: loading
},
solanaPaymentOptions: {
options: solanaOptions,
isLoading: loading
},
depositAddressOptions: {
options: depositOptions,
loading
}
};
}
function addPassthroughTokens(options, passthroughTokens, passthroughAddress) {
if (passthroughTokens == null) return;
for (const option of options) {
const tok = option.balance.token;
if (option.disabledReason != null) continue;
const found = passthroughTokens.find(
(t) => t.address === tok.token && t.chain == tok.chainId
);
if (found == null) continue;
option.passthroughAddress = passthroughAddress;
}
}
export { addPassthroughTokens, usePaymentOptions };
//# sourceMappingURL=usePaymentOptions.js.map