Skip to content

Commit

Permalink
refactor swap component
Browse files Browse the repository at this point in the history
  • Loading branch information
abcrane123 committed Jun 12, 2024
1 parent c2fae82 commit 5cd8a44
Show file tree
Hide file tree
Showing 7 changed files with 188 additions and 203 deletions.
150 changes: 54 additions & 96 deletions site/docs/pages/swap/swap.mdx
Original file line number Diff line number Diff line change
@@ -1,56 +1,21 @@
import { Swap } from '../../../../src/swap';
import { Swap, SwapAmountInputV2, SwapButton } from '../../../../src/swap';
import App from '../App';
import SwapContainer from '../../components/SwapContainer.tsx';

# `<Swap />`

The `Swap` component is a comprehensive interface for users to execute token swaps. It includes two instances of the `SwapAmountInput` component, enabling users to specify the amount of tokens to sell and buy. The `SwapTokensButton` component facilitates the swapping of selected tokens with a single click, dynamically interchanging the tokens in the "Sell" and "Buy" fields. Additionally, the component features a "Swap" button for initiating the transaction.
The `Swap` component is a comprehensive interface for users to execute token swaps. It includes two instances of the `SwapAmountInputV2` component, enabling users to specify the amount of tokens to sell and buy. The `SwapTokensButton` component facilitates the swapping of selected tokens with a single click, dynamically interchanging the tokens in the "Sell" and "Buy" fields. Additionally, the component features a "Swap" button for initiating the transaction.

## Usage

:::code-group

```tsx [code]
<Swap
fromAmount={fromAmount}
fromToken={fromToken}
fromTokenBalance={fromTokenBalance}
onSubmit={onSubmit}
setFromAmount={setFromAmount}
setFromToken={setFromToken}
setToToken={setToToken}
swappableTokens={[
{
name: 'Ethereum',
address: '',
symbol: 'ETH',
decimals: 18,
image: 'https://wallet-api-production.s3.amazonaws.com/uploads/tokens/eth_288.png',
chainId: 8453,
},
{
name: 'USDC',
address: '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913',
symbol: 'USDC',
decimals: 6,
image:
'https://d3r81g40ycuhqg.cloudfront.net/wallet/wais/44/2b/442b80bd16af0c0d9b22e03a16753823fe826e5bfd457292b55fa0ba8c1ba213-ZWUzYjJmZGUtMDYxNy00NDcyLTg0NjQtMWI4OGEwYjBiODE2',
chainId: 8453,
},
{
name: 'Dai',
address: '0x50c5725949a6f0c72e6c4a641f24049a917db0cb',
symbol: 'DAI',
decimals: 18,
image:
'https://d3r81g40ycuhqg.cloudfront.net/wallet/wais/d0/d7/d0d7784975771dbbac9a22c8c0c12928cc6f658cbcf2bbbf7c909f0fa2426dec-NmU4ZWViMDItOTQyYy00Yjk5LTkzODUtNGJlZmJiMTUxOTgy',
chainId: 8453,
},
]}
toAmount={toAmount}
toToken={toToken}
toTokenBalance={toTokenBalance}
/>
<Swap>
<SwapAmountInputV2 type="from" label="Sell" token={fromToken} />
<SwapAmountInputV2 type="to" label="Buy" token={toToken} />
<SwapButton onSubmit={onSubmit} fromToken={fromToken} toToken={toToken} />
</Swap>
```

```html [return html]
Expand All @@ -60,60 +25,53 @@ The `Swap` component is a comprehensive interface for users to execute token swa
:::

<App>
<SwapContainer>
{(
fromAmount,
fromToken,
fromTokenBalance,
setFromAmount,
setFromToken,
setToToken,
toAmount,
toToken,
toTokenBalance,
) => (
<Swap
fromAmount={fromAmount}
fromToken={fromToken}
fromTokenBalance={fromTokenBalance}
onSubmit={() => {}}
setFromAmount={setFromAmount}
setFromToken={setFromToken}
setToToken={setToToken}
swappableTokens={[
{
name: 'Ethereum',
address: '',
symbol: 'ETH',
decimals: 18,
image: 'https://wallet-api-production.s3.amazonaws.com/uploads/tokens/eth_288.png',
chainId: 8453,
},
{
name: 'USDC',
address: '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913',
symbol: 'USDC',
decimals: 6,
image:
'https://d3r81g40ycuhqg.cloudfront.net/wallet/wais/44/2b/442b80bd16af0c0d9b22e03a16753823fe826e5bfd457292b55fa0ba8c1ba213-ZWUzYjJmZGUtMDYxNy00NDcyLTg0NjQtMWI4OGEwYjBiODE2',
chainId: 8453,
},
{
name: 'Dai',
address: '0x50c5725949a6f0c72e6c4a641f24049a917db0cb',
symbol: 'DAI',
decimals: 18,
image:
'https://d3r81g40ycuhqg.cloudfront.net/wallet/wais/d0/d7/d0d7784975771dbbac9a22c8c0c12928cc6f658cbcf2bbbf7c909f0fa2426dec-NmU4ZWViMDItOTQyYy00Yjk5LTkzODUtNGJlZmJiMTUxOTgy',
chainId: 8453,
},
]}
toAmount={toAmount}
toToken={toToken}
toTokenBalance={toTokenBalance}
/>
)}
</SwapContainer>
<Swap>
<SwapAmountInputV2
type="from"
label="Sell"
token={{
name: 'Ethereum',
address: '',
symbol: 'ETH',
decimals: 18,
image: 'https://wallet-api-production.s3.amazonaws.com/uploads/tokens/eth_288.png',
chainId: 8453,
}}
/>
<SwapAmountInputV2
type="to"
label="Buy"
token={{
name: 'Dai',
address: '0x50c5725949a6f0c72e6c4a641f24049a917db0cb',
symbol: 'DAI',
decimals: 18,
image:
'https://d3r81g40ycuhqg.cloudfront.net/wallet/wais/d0/d7/d0d7784975771dbbac9a22c8c0c12928cc6f658cbcf2bbbf7c909f0fa2426dec-NmU4ZWViMDItOTQyYy00Yjk5LTkzODUtNGJlZmJiMTUxOTgy',
chainId: 8453,
}}
/>
<SwapButton
onSubmit={() => {}}
fromToken={{
name: 'Ethereum',
address: '',
symbol: 'ETH',
decimals: 18,
image: 'https://wallet-api-production.s3.amazonaws.com/uploads/tokens/eth_288.png',
chainId: 8453,
}}
toToken={{
name: 'Dai',
address: '0x50c5725949a6f0c72e6c4a641f24049a917db0cb',
symbol: 'DAI',
decimals: 18,
image:
'https://d3r81g40ycuhqg.cloudfront.net/wallet/wais/d0/d7/d0d7784975771dbbac9a22c8c0c12928cc6f658cbcf2bbbf7c909f0fa2426dec-NmU4ZWViMDItOTQyYy00Yjk5LTkzODUtNGJlZmJiMTUxOTgy',
chainId: 8453,
}}
/>
</Swap>
</App>

## Props
Expand Down
112 changes: 21 additions & 91 deletions src/swap/components/Swap.tsx
Original file line number Diff line number Diff line change
@@ -1,99 +1,29 @@
import { useCallback } from 'react';
import { SwapAmountInput } from './SwapAmountInput';
import { SwapReact, SwapTokensButtonReact } from '../types';
import { useMemo, useState } from 'react';
import type { SwapReact } from '../types';
import { SwapContext } from '../context';

const swapIcon = (
<svg
width="16"
height="17"
viewBox="0 0 16 17"
fill="none"
xmlns="http://www.w3.org/2000/svg"
data-testid="SwapIcon"
>
<g clip-path="url(#clip0_2077_4627)">
<path
d="M14.5659 4.93434L13.4345 6.06571L11.8002 4.43139L11.8002 10.75L10.2002 10.75L10.2002 4.43139L8.56592 6.06571L7.43455 4.93434L11.0002 1.36865L14.5659 4.93434ZM8.56592 12.0657L5.00023 15.6314L1.43455 12.0657L2.56592 10.9343L4.20023 12.5687L4.20023 6.25002L5.80023 6.25002L5.80023 12.5687L7.43455 10.9343L8.56592 12.0657Z"
fill="#0A0B0D"
/>
</g>
<defs>
<clipPath id="clip0_2077_4627">
<rect width="16" height="16" fill="white" transform="translate(0 0.5)" />
</clipPath>
</defs>
</svg>
);
export function Swap({ account, children }: SwapReact) {
const [fromAmount, setFromAmount] = useState('');
const [toAmount, setToAmount] = useState('');

export function SwapTokensButton({ onClick }: SwapTokensButtonReact) {
return (
<div
className="absolute left-2/4 top-2/4 flex h-12 w-12 -translate-x-2/4 -translate-y-2/4 cursor-pointer items-center justify-center rounded-[50%] border border-solid border-gray-100 bg-white"
onClick={onClick}
data-testid="SwapTokensButton"
>
{swapIcon}
</div>
);
}

export function Swap({
fromAmount,
fromToken,
fromTokenBalance,
onSubmit,
setFromAmount,
setFromToken,
setToToken,
swappableTokens,
toAmount,
toToken,
toTokenBalance,
}: SwapReact) {
const handleSwapTokensClick = useCallback(() => {
if (!toToken || !fromToken) {
return;
}
const prevFromToken = fromToken;
setFromToken(toToken);
setToToken(prevFromToken);
}, [fromToken, toToken, setFromToken, setToToken]);
const value = useMemo(() => {
return {
fromAmount,
setFromAmount,
toAmount,
setToAmount,
account,
};
}, [account, fromAmount, setFromAmount, setToAmount, toAmount]);

return (
<div className="flex w-[400px] flex-col rounded-xl bg-white">
<label className="box-border w-full border-b border-solid p-4 text-base font-semibold leading-6 text-[#030712] shadow-[0px_4px_4px_0px_rgba(3,7,18,0.05)]">
Swap
</label>
<div className="relative flex flex-col">
<SwapAmountInput
amount={fromAmount}
label="Sell"
setAmount={setFromAmount}
setToken={setFromToken}
token={fromToken}
tokenBalance={fromTokenBalance}
swappableTokens={swappableTokens}
/>
<SwapTokensButton onClick={handleSwapTokensClick} />
<SwapAmountInput
amount={toAmount}
disabled
displayMaxButton={false}
label="Buy"
setToken={setToToken}
swappableTokens={swappableTokens}
token={toToken}
tokenBalance={toTokenBalance}
/>
</div>
<div className="w-full p-4">
<button
className="w-full rounded-[100px] bg-blue-700 px-4 py-3 text-base font-medium leading-6 text-white"
onClick={onSubmit}
>
<SwapContext.Provider value={value}>
<div className="flex w-[400px] flex-col rounded-xl bg-white">
<label className="box-border w-full border-b border-solid p-4 text-base font-semibold leading-6 text-[#030712] shadow-[0px_4px_4px_0px_rgba(3,7,18,0.05)]">
Swap
</button>
</label>
{children}
</div>
</div>
</SwapContext.Provider>
);
}
51 changes: 51 additions & 0 deletions src/swap/components/SwapAmountInputV2.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { useCallback, useContext, useMemo } from 'react';

import { isValidAmount } from '../utils';
import { TokenChip } from '../../token';
import { SwapContext } from '../context';
import type { SwapAmountInputReact } from '../types';

export function SwapAmountInputV2({ label, token, type }: SwapAmountInputReact) {
const { fromAmount, setFromAmount, toAmount, setToAmount } = useContext(SwapContext);

const amount: string = useMemo(() => {
if (type === 'to') {
return toAmount;
}
return fromAmount;
}, [type, toAmount, fromAmount]);

const setAmount: (a: string) => void = useMemo(() => {
if (type === 'to') {
return setToAmount;
}
return setFromAmount;
}, [type, setToAmount, setFromAmount]);

const handleAmountChange = useCallback((event: React.ChangeEvent<HTMLInputElement>) => {
if (isValidAmount(event.target.value)) {
setAmount?.(event.target.value);
}
}, []);

return (
<div
className="box-border flex w-full flex-col items-start gap-[11px] border-b border-solid bg-[#FFF] p-4"
data-testid="ockSwapAmountInput_Container"
>
<div className="flex w-full items-center justify-between">
<label className="text-sm font-semibold text-[#030712]">{label}</label>
</div>
<div className="flex w-full items-center justify-between">
{token && <TokenChip token={token} />}
</div>
<input
className="w-full border-[none] bg-transparent text-5xl text-[black]"
data-testid="ockSwapAmountInput_Input"
onChange={handleAmountChange}
placeholder="0"
value={amount}
/>
</div>
);
}
23 changes: 23 additions & 0 deletions src/swap/components/SwapButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { useCallback, useContext } from 'react';
import { SwapContext } from '../context';
import type { SwapButtonReact } from '../types';

export function SwapButton({ fromToken, onSubmit, toToken }: SwapButtonReact) {
const { account, fromAmount } = useContext(SwapContext);

const handleSubmit = useCallback(() => {
if (account && fromToken && toToken && fromAmount) {
onSubmit({ fromAddress: account.address, from: fromToken, to: toToken, amount: fromAmount });
}
}, []);
return (
<div className="w-full p-4">
<button
className="w-full rounded-[100px] bg-blue-700 px-4 py-3 text-base font-medium leading-6 text-white"
onClick={handleSubmit}
>
Swap
</button>
</div>
);
}
10 changes: 10 additions & 0 deletions src/swap/context.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { createContext } from 'react';
import { SwapContextType } from './types';

export const SwapContext = createContext<SwapContextType>({
fromAmount: '',
setFromAmount: () => {},
toAmount: '',
setToAmount: () => {},
account: undefined,
});
3 changes: 3 additions & 0 deletions src/swap/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
// 🌲☀️🌲
export { getSwapQuote } from './core/getSwapQuote';
export { Swap } from './components/Swap'
export { SwapAmountInput } from './components/SwapAmountInput';
export { SwapAmountInputV2 } from './components/SwapAmountInputV2';
export { SwapButton } from './components/SwapButton';
export type {
BuildSwapTransactionParams,
BuildSwapTransactionResponse,
Expand Down
Loading

0 comments on commit 5cd8a44

Please sign in to comment.