Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Native Send Component #1874

Draft
wants to merge 55 commits into
base: main
Choose a base branch
from
Draft

feat: Native Send Component #1874

wants to merge 55 commits into from

Conversation

brendan-defi
Copy link
Contributor

What changed? Why?

Notes to reviewers

How has it been tested?

Copy link

vercel bot commented Jan 24, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
onchainkit-coverage ❌ Failed (Inspect) Jan 30, 2025 3:18am
onchainkit-playground ❌ Failed (Inspect) Jan 30, 2025 3:18am
onchainkit-routes ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jan 30, 2025 3:18am

</div>
<div className="text-right">
{showAction ? (
<span
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why <span > here? Probably should be a button if it's clickable

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can't have a button within a button, and sometimes the whole container is a button

'overflow-hidden text-ellipsis whitespace-nowrap text-left',
)}
>
{token.name.trim()}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curious what the need for trim() is here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

some tokens have whitespace at the front / end for style /s. we don't want that

context,
});

const activeStep = useMemo(() => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see what's going on here but it's quite hard to reason about having not built it myself.

I think the easiest thing would probably be breaking this into internal subcomponents for each step (we'd have to think about the right names, not sure about that). What do you think about that?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

e.g. it'd be easier to reason about that you're rendering the recipient search/options if this was in a component:

        <>
          <SendAddressInput
            selectedRecipientAddress={context.selectedRecipientAddress}
            recipientInput={context.recipientInput}
            setRecipientInput={context.setRecipientInput}
            handleRecipientInputChange={context.handleRecipientInputChange}
          />
          {context.validatedRecipientAddress && (
            <SendAddressSelector
              address={context.validatedRecipientAddress}
              senderChain={context.senderChain}
              handleAddressSelection={context.handleAddressSelection}
            />
          )}
        </>

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah this is exactly the kind of thing i was hoping to get your thoughts on. i can see why this would be confusing.

is the suggestion to put all the conditionals into the sub-components, so it looks like (not real component names):

if (!isInitialized) {
  return null;
}

return (
  <FundWallet />
  <AddressSelection />
  <TokenSelection />
  <AmountInputAndConfirmation />
)

or keep the conditionals here, but just pull all the return values into sub-components?

import { Address, Avatar, Identity, Name } from '@/identity';
import { background, border, cn, pressable } from '@/styles/theme';
import { useCallback } from 'react';
import type { Address as AddressType, Chain } from 'viem';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would avoid the rename if possible

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't think it's possible right? I need the Address component from identity

handleAddressSelection: (address: AddressType) => void;
};

export function SendAddressSelector({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do you feel about SendAddressOption?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as teh component name? i like Selector better because it signals that the user needs to interact with it


export function SendAddressSelector({
address,
senderChain,
Copy link
Contributor

@dschlabach dschlabach Jan 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd probably drop the senderChain here and get it from context (or global context) since it doesn't seem like something you're going to want to pass to each option, even if you use subcomponents?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

right now i'm taking a props-maxi approach, almost entirely avoiding using context in the subcomponents. i think this is better devex because it enables easier customization. alternative would be to export the provider. not sure what's better

import { cn, color, text } from '@/styles/theme';
import { useSendContext } from '@/wallet/components/WalletAdvancedSend/components/SendProvider';

export function SendAmountInput({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you move this to the bottom so it's easier to read when scanning? (e.g. exports at bottom)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

like just function declaration and then at the bottom do the exports?

tbh i find that way much more confusing because i like to know immediately whether the thing i'm looking at is "part of the reason for the file" or "just a helper in the file", but not a hill i'll die on


export function SendButton({
label = 'Continue',
senderChain,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd drop chain here as well

errorOverride,
cryptoAmount,
selectedToken,
callData,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd drop and get from context - otherwise people should just use <Transaction />

'disabled' | 'pendingOverride' | 'successOverride' | 'errorOverride'
>;

export function SendButton({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you move to bottom so it's easier to find the export?


const defaultSuccessHandler = useCallback(
(receipt: TransactionReceipt | undefined) => {
// SW will have txn id so open in wallet
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Had a bit of a hard time understanding this context
// SW will have txn id so open in wallet

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry, i just copied directly from Transaction.

smart wallet transactions don't have the same structure as EOAs. so the way we link to them is different

Comment on lines 164 to 169
useExchangeRate({
token: selectedToken,
selectedInputType,
setExchangeRate,
setExchangeRateLoading,
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where does this get set?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/and read?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

useExchangeRate sets the exchange rate value.

exchangeRate is read by SendAmountInput

ethBalance: number | undefined;
tokenBalances: PortfolioTokenWithFiatValue[] | undefined;

// Recipient Address Context
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the comments, they're helpful

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

Successfully merging this pull request may close these issues.

2 participants