Skip to content

Commit

Permalink
Don't auto reset ExecuteTokenTransferButton on error
Browse files Browse the repository at this point in the history
Auto reset triggers unconditionally when exchange rates update, causing
any ExecuteTokenTransferButton error to disappear. But instead, we want
the error to persist so the user may copy it and submit it to us.
  • Loading branch information
ryanberckmans committed Jul 26, 2024
1 parent d9edfe2 commit eeb3e68
Show file tree
Hide file tree
Showing 2 changed files with 3 additions and 3 deletions.
4 changes: 2 additions & 2 deletions packages/interface/src/Pay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -596,7 +596,7 @@ const PayInner: React.FC<PayInnerProps> = ({ checkoutSettings }) => {
{...computedExecuteTokenTransferButtonPropValues}
tt={tt}
nativeTokenTransferProxy={checkoutSettings.nativeTokenTransferProxy}
autoReset={true}
autoReset={status?.error ? false : true}
loadForeverOnTransactionFeeUnaffordableError={true}
label={`${checkoutVerbCapitalized} Now`}
successLabel="Paid ✅"
Expand All @@ -615,7 +615,7 @@ const PayInner: React.FC<PayInnerProps> = ({ checkoutSettings }) => {
)}
{retryButton}
</div>;
}, [checkoutSettings.nativeTokenTransferProxy, setStatus, checkoutVerbCapitalized, activeDemoAccount, retryButton, executeTokenTransferButtonPropValues]);
}, [checkoutSettings.nativeTokenTransferProxy, status?.error, setStatus, checkoutVerbCapitalized, activeDemoAccount, retryButton, executeTokenTransferButtonPropValues]);

const paymentScreen: false | JSX.Element = useMemo(() => !statusIsSuccess && <div className={`${selectingPaymentMethod ? 'hidden' : '' /* WARNING here we hide the payment screen when selecting payment method instead of destroying it. This avoids an ExecuteTokenTransferButton remount each time the payment method changes, which is a mechanism to test reset logic and code paths. */}`}>
<div className="w-full py-6">
Expand Down
2 changes: 1 addition & 1 deletion packages/interface/src/transactions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ export type ExecuteTokenTransferButtonProps = {
tt: TokenTransfer | undefined; // the token transfer this button will execute. If undefined, the button will appear to be loading forever, and the passed setStatus will never be called. WARNING ExecuteTokenTransferButton doesn't support arbitrary ongoing changes to the props TokenTransfer. See ExecuteTokenTransferButtonStatus.activeTokenTransfer.
nativeTokenTransferProxy: 'never' | 'prefer' | 'require'; // 3cities supports automatic use of a built-in proxy that emits an ERC20-compliant Transfer event for a native token transfer. This proxy exists because generalized offchain detection of ETH transfers (eg. when using smart contract wallets) can't be done using the ethrpc api, and can only be done with non-standard tracing APIs. This button can automatically route native token transfers through our built-in proxy, such that the transfers are detectable by monitoring for Transfer events. Our built-in proxy is a stateless hyperstructure that never has custody of funds and simply forwards any ETH sent to the specified recipient and emits a Transfer event, using about 50% more gas than a standard ETH transfer. A permament solution to this problem has been proposed via EIP-7708: ETH transfers emit a log. If set to 'never', this proxy will never be used and native token transfers will occur ordinarily (standard ETH transfer). If 'prefer', the proxy will be used if it's available on the chain where the native token transfer is being executed. If 'require', the proxy must be used and native token transfers attempted on chains where the proxy is unavailable will result in an error status.
onClickPassthrough?: () => void; // iff defined, when the button is clicked, that click will be ignored internally and instead passed through to this callback. This allows clients to reuse this same button for other purposes. For example, clicking the button sign a message. onClickPassthrough is interoperable (works with) with other props, such as `disabled` and `showLoadingSpinnerWhenDisabled`. NB clicks that are passed through to onClickPassthrough are completely ignored internally, eg. a click forwarded to onClickPassthrough will not result in status.buttonClickedAtLeastOnce being set to true
autoReset?: true; // if set, the button will automatically call its own status.reset to update cached token transfer details when props.tt changes, but only if the user isn't currently signing the transaction or has already signed the transaction, in which case the button is never auto-reset but can still be reset manually by the client. WARNING automatic resets trigger a new status, so clients must ensure a new status doesn't unconditionally compute new tt object, or else an infinite async render loop will occur (new tt -> auto reset -> async reset -> new status -> repeat)
autoReset?: boolean; // iff true, the button will automatically call its own status.reset to update cached token transfer details when props.tt changes, but only if the user isn't currently signing the transaction or has already signed the transaction, in which case the button is never auto-reset but can still be reset manually by the client. WARNING automatic resets trigger a new status, so clients must ensure a new status doesn't unconditionally compute new tt object, or else an infinite async render loop will occur (new tt -> auto reset -> async reset -> new status -> repeat)
autoClickIfNeverClicked?: boolean; // iff true, the button will automatically click itself iff it's ready to click and has never been clicked (ie. !buttonClickedAtLeastOnce, noting that buttonClickedAtLeastOnce is reset to false on status.reset()). For example, this allows a client to have the user's first click perform some other action (such as siging a message) and then automatically click the button to execute the token transfer following the resolution of that action, preventing the user from having to click the button twice. WARNING an automatic click/execute triggers a new status, so clients must ensure a new status doesn't unconditionally automatically click/execute again (which can only occur if the new status triggers an unconditional reset which resets buttonClickedAtLeastOnce), or else an infinite async render loop will occur (auto execute -> new status -> async reset -> repeat)
loadForeverOnTransactionFeeUnaffordableError?: true; // when the button detects that the user can't afford the transaction fee for the active token transfer, the button UI normally shows an error, but if this is set, instead will show itself as loading. When the fee is unaffordable, regardless if this set, `status.error` will be an instanceof TransactionFeeUnaffordableError, which allows the client to detect fee unaffordability and optionally, replace the active transfer with one that the user may be able to afford. The point of this flag is to enable the client to replace the active transfer on fee unaffordability without the janky intermediate UI state of the button showing an error. WARNING TransactionFeeUnaffordableError is detected on a case-by-case basis depending on the wallet's implementation, so the transaction might be (or end up being) unaffordable even if there's no error or the error isn't an instanceof TransactionFeeUnaffordableError. Ie. TransactionFeeUnaffordableError is subject to false negatives, but not false positives.
label: string; // label to put on the button, eg. "Pay Now", "Donate Now", "Place your order"
Expand Down

0 comments on commit eeb3e68

Please sign in to comment.