@0xsequence/connect
Version:
Connect package for Sequence Web SDK
157 lines • 7.61 kB
JavaScript
;
'use client';
Object.defineProperty(exports, "__esModule", { value: true });
exports.useWaasFeeOptions = useWaasFeeOptions;
const hooks_1 = require("@0xsequence/hooks");
const indexer_1 = require("@0xsequence/indexer");
const react_1 = require("react");
const viem_1 = require("viem");
const wagmi_1 = require("wagmi");
const deferred_js_1 = require("../utils/deferred.js");
// --- Shared State Management ---
let sharedPendingConfirmation = undefined;
let sharedDeferred = undefined;
let listeners = [];
const notifyListeners = (state) => listeners.forEach(listener => listener(state));
/**
* Hook for handling WaaS (Wallet as a Service) fee options for unsponsored transactions
*
* This hook provides functionality to:
* - Get available fee options for a transaction in Native Token and ERC20's
* - Provide user wallet balances for each fee option
* - Confirm or reject fee selections
*
* @param options - Configuration options for the hook {@link WaasFeeOptionsConfig}
* @returns Array containing the confirmation state and control functions {@link UseWaasFeeOptionsReturnType}
*
* @example
* ```tsx
* // Use the hook with default balance checking, this will fetch the user's wallet balances for each fee option and provide them in the UseWaasFeeOptionsReturn
* const [
* pendingFeeOptionConfirmation,
* confirmPendingFeeOption,
* rejectPendingFeeOption
* ] = useWaasFeeOptions();
*
* // Or skip balance checking if needed
* // const [pendingFeeOptionConfirmation, confirmPendingFeeOption, rejectPendingFeeOption] =
* // useWaasFeeOptions({ skipFeeBalanceCheck: true });
*
* const [selectedFeeOptionTokenName, setSelectedFeeOptionTokenName] = useState<string>();
* const [feeOptionAlert, setFeeOptionAlert] = useState<AlertProps>();
*
* // Initialize with first option when fee options become available
* useEffect(() => {
* if (pendingFeeOptionConfirmation) {
* console.log('Pending fee options: ', pendingFeeOptionConfirmation.options)
* }
* }, [pendingFeeOptionConfirmation]);
*
* ```
*/
function useWaasFeeOptions(options) {
const { skipFeeBalanceCheck = false } = options || {};
const connections = (0, wagmi_1.useConnections)();
const waasConnector = connections.find((c) => c.connector.id.includes('waas'))?.connector;
const [pendingFeeOptionConfirmation, setPendingFeeOptionConfirmation] = (0, react_1.useState)(sharedPendingConfirmation);
const indexerClient = (0, hooks_1.useIndexerClient)(options?.chainIdOverride ? options.chainIdOverride : (connections[0]?.chainId ?? 1));
/**
* Confirms the selected fee option
* @param id - The fee confirmation ID
* @param feeTokenAddress - The address of the token to use for fee payment (null for native token)
*/
function confirmPendingFeeOption(id, feeTokenAddress) {
if (sharedDeferred && sharedPendingConfirmation?.id === id) {
sharedDeferred.resolve({ id, feeTokenAddress, confirmed: true });
sharedDeferred = undefined;
notifyListeners(undefined);
}
}
/**
* Rejects the current fee option confirmation
* @param id - The fee confirmation ID to reject
*/
function rejectPendingFeeOption(id) {
if (sharedDeferred && sharedPendingConfirmation?.id === id) {
sharedDeferred.resolve({ id, feeTokenAddress: undefined, confirmed: false });
sharedDeferred = undefined;
sharedPendingConfirmation = undefined;
notifyListeners(undefined);
}
}
(0, react_1.useEffect)(() => {
// Subscribe to shared state changes
listeners.push(setPendingFeeOptionConfirmation);
// Set initial state in case it changed between component initialization and effect execution
setPendingFeeOptionConfirmation(sharedPendingConfirmation);
return () => {
listeners = listeners.filter(l => l !== setPendingFeeOptionConfirmation);
};
}, []);
(0, react_1.useEffect)(() => {
if (!waasConnector) {
return;
}
const waasProvider = waasConnector.sequenceWaasProvider;
if (!waasProvider) {
return;
}
const originalHandler = waasProvider.feeConfirmationHandler;
waasProvider.feeConfirmationHandler = {
async confirmFeeOption(id, options, txs, chainId) {
const pending = new deferred_js_1.Deferred();
// Store the deferred promise in the shared scope
sharedDeferred = pending;
// Clear any previous stale state immediately
sharedPendingConfirmation = undefined;
notifyListeners(undefined);
const accountAddress = connections[0]?.accounts[0];
if (!accountAddress) {
throw new Error('No account address available');
}
if (!skipFeeBalanceCheck) {
const optionsWithBalances = await Promise.all(options.map(async (option) => {
if (option.token.contractAddress) {
const tokenBalances = await indexerClient.getTokenBalancesByContract({
filter: {
accountAddresses: [accountAddress],
contractStatus: indexer_1.ContractVerificationStatus.ALL,
contractAddresses: [option.token.contractAddress]
},
omitMetadata: true
});
const tokenBalance = tokenBalances.balances[0]?.balance;
return {
...option,
balanceFormatted: option.token.decimals
? (0, viem_1.formatUnits)(BigInt(tokenBalances.balances[0]?.balance ?? '0'), option.token.decimals)
: (tokenBalances.balances[0]?.balance ?? '0'),
balance: tokenBalances.balances[0]?.balance ?? '0',
hasEnoughBalanceForFee: tokenBalance ? BigInt(option.value) <= BigInt(tokenBalance) : false
};
}
const nativeBalance = await indexerClient.getNativeTokenBalance({ accountAddress });
return {
...option,
balanceFormatted: (0, viem_1.formatUnits)(BigInt(nativeBalance.balance.balance), 18),
balance: nativeBalance.balance.balance,
hasEnoughBalanceForFee: BigInt(option.value) <= BigInt(nativeBalance.balance.balance)
};
}));
sharedPendingConfirmation = { id, options: optionsWithBalances, chainId };
notifyListeners(sharedPendingConfirmation);
}
else {
sharedPendingConfirmation = { id, options, chainId };
notifyListeners(sharedPendingConfirmation);
}
return pending.promise;
}
};
return () => {
waasProvider.feeConfirmationHandler = originalHandler;
};
}, [waasConnector, indexerClient]);
return [pendingFeeOptionConfirmation, confirmPendingFeeOption, rejectPendingFeeOption];
}
//# sourceMappingURL=useWaasFeeOptions.js.map