8000 feat: refactor and add more networks to the token details page swap w… by cowdan · Pull Request #5900 · cowprotocol/cowswap · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

feat: refactor and add more networks to the token details page swap w… #5900

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

Merged
merged 6 commits into from
Jun 30, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
184 changes: 108 additions & 76 deletions apps/cow-fi/components/SwapWidget.tsx
10000
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
import React, { useState, useEffect, ChangeEvent } from 'react'
import { useState, useEffect, ChangeEvent, JSX } from 'react'

import { Color } from '@cowprotocol/ui'

import Image from 'next/image'
import { transparentize } from 'polished'
import styled from 'styled-components/macro'

import { LinkWithUtmComponent } from 'modules/utm/components'

import { Button } from '@/components/Button'
import { CONFIG } from '@/const/meta'
import {
Network,
NETWORK_DEFAULT_BUY_TOKEN_MAP,
NETWORK_DEFAULT_SELL_TOKEN_MAP,
NETWORK_ID_MAP,
NETWORK_IMAGE_MAP,
NETWORK_MAP,
} from '@/const/networkMap'

type TabProps = {
active: boolean
Expand Down Expand Up @@ -193,128 +202,151 @@ type SwapWidgetProps = {
platforms: Platforms
}

enum Networks {
ETHEREUM = 'ethereum',
XDAI = 'xdai',
type Tab = 'Buy' | 'Sell'
const DEFAULT_TAB: Tab = 'Buy'
const DEFAULT_NETWORK: Network = 'ethereum'

const getBuyAndSellTokens = (
activeTab: Tab,
network: Network,
contractAddress: string,
): { sellToken: string; buyToken: string } => {
if (activeTab === 'Buy') {
return {
sellToken: NETWORK_DEFAULT_SELL_TOKEN_MAP[network],
buyToken: contractAddress,
}
}

return {
sellToken: contractAddress,
buyToken: NETWORK_DEFAULT_BUY_TOKEN_MAP[network],
}
}

const NETWORK_MAP: { [key: string]: string } = {
[Networks.ETHEREUM]: 'Ethereum',
[Networks.XDAI]: 'Gnosis Chain',
const Tabs = ({ activeTab, setActiveTab }: { activeTab: Tab; setActiveTab: (tab: Tab) => void }): JSX.Element => {
return (
<TabContainer>
<Tab => setActiveTab('Buy')} active={activeTab === 'Buy'}>
Buy
</Tab>
<Tab => setActiveTab('Sell')} active={activeTab === 'Sell'}>
Sell
</Tab>
</TabContainer>
)
}

const WXDAI = '0xe91d153e0b41518a2ce8dd3d7944fa863463a97d'
const WETH = ['0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', '0x6a023ccd1ff6f2045c3309768ead9e68f978f6e1']
function DropdownNetworkOption({
network,
handleSelect,
}: {
network: Network
handleSelect: (network: Network) => void
}): JSX.Element {
const width = 20
const height = 20

export const SwapWidget = ({ tokenId, tokenSymbol, tokenImage, platforms }: SwapWidgetProps) => {
const [activeTab, setActiveTab] = useState('Buy')
const [network, setNetwork] = useState<string | null>(null)
return (
<DropdownOption => handleSelect(network)}>
<Image src={NETWORK_IMAGE_MAP[network]} alt={NETWORK_MAP[network]} width={width} height={height} />
{NETWORK_MAP[network]}
</DropdownOption>
)
}

const getDropdownBody = (platforms: Platforms, handleSelect: (network: Network) => void): JSX.Element => {
const { ethereum, xdai, base, 'arbitrum-one': arbitrum, avalanche, 'polygon-pos': polygon } = platforms

return (
<DropdownBody>
{ethereum?.contractAddress && <DropdownNetworkOption network="ethereum" handleSelect={handleSelect} />}
{base?.contractAddress && <DropdownNetworkOption network="base" handleSelect={handleSelect} />}
{arbitrum?.contractAddress && <DropdownNetworkOption network="arbitrum-one" handleSelect={handleSelect} />}
{polygon?.contractAddress && <DropdownNetworkOption network="polygon-pos" handleSelect={handleSelect} />}
{avalanche?.contractAddress && <DropdownNetworkOption network="avalanche" handleSelect={handleSelect} />}
{xdai?.contractAddress && <DropdownNetworkOption network="xdai" handleSelect={handleSelect} />}
</DropdownBody>
)
}

function getNetworkFromPlatforms(platforms: Platforms): Network {
const { ethereum, xdai, base, 'arbitrum-one': arbitrum, avalanche, 'polygon-pos': polygon } = platforms

if (ethereum?.contractAddress) return 'ethereum'
if (base?.contractAddress) return 'base'
if (arbitrum?.contractAddress) return 'arbitrum-one'
if (polygon?.contractAddress) return 'polygon-pos'
if (avalanche?.contractAddress) return 'avalanche'
if (xdai?.contractAddress) return 'xdai'

return 'ethereum'
}

export const SwapWidget = ({ tokenId, tokenSymbol, tokenImage, platforms }: SwapWidgetProps): JSX.Element => {
const [activeTab, setActiveTab] = useState<Tab>(DEFAULT_TAB)
const [network, setNetwork] = useState<Network>(DEFAULT_NETWORK)
const [amount, setAmount] = useState(0)

const [isOpen, setIsOpen] = useState(false)
const handleSelect = (network: string | null) => {

const handleSelect = (network: Network): void => {
setNetwork(network)
setIsOpen(false)
}

// set initial network based on the available platforms
useEffect(() => {
// set initial network based on the available platforms
if (platforms.ethereum.contractAddress) setNetwork(Networks.ETHEREUM)
else if (platforms.xdai.contractAddress) setNetwork(Networks.XDAI)
setNetwork(getNetworkFromPlatforms(platforms))
}, [platforms])

const handleInputChange = (event: ChangeEvent<any>) => {
const handleInputChange = (event: ChangeEvent<HTMLInputElement>): void => {
let value = event.target.value

// Remove leading minus sign if present
if (value.startsWith('-')) {
value = value.slice(1)
}

if (value === '' || (parseFloat(value) >= 0 && !isNaN(value))) {
setAmount(value)
if (value === '' || (parseFloat(value) >= 0 && !isNaN(parseFloat(value)))) {
setAmount(parseFloat(value))
}
}

const onSwap = () => {
const getSwapUrl = (): string => {
if (network && platforms[network]) {
const networkId = network === 'xdai' ? 100 : 1
const networkId = NETWORK_ID_MAP[network as Network]
const contractAddress = platforms[network].contractAddress

let sellToken, buyToken
if (activeTab === 'Buy') {
sellToken = networkId === 100 ? 'WXDAI' : 'WETH'
buyToken = contractAddress

if (contractAddress === WXDAI) {
sellToken = 'XDAI'
}

if (WETH.includes(contractAddress)) {
sellToken = networkId === 100 ? 'WXDAI' : 'ETH'
}
} else {
sellToken = contractAddress
buyToken = networkId === 100 ? 'WXDAI' : 'WETH'

if (contractAddress === WXDAI) {
buyToken = 'XDAI'
}

if (WETH.includes(contractAddress)) {
buyToken = networkId === 100 ? 'WXDAI' : 'ETH'
}
}
const { sellToken, buyToken } = getBuyAndSellTokens(activeTab, network as Network, contractAddress)

return `https://swap.cow.fi/#/${networkId}/swap/${sellToken}/${buyToken}?${activeTab.toLowerCase()}Amount=${amount}`
} else {
return '#'
}
}

const networkImage = NETWORK_IMAGE_MAP[network as Network]
const networkName = NETWORK_MAP[network as Network]

return (
<Wrapper>
<TabContainer>
<Tab => setActiveTab('Buy')} active={activeTab === 'Buy'}>
Buy
</Tab>
<Tab => setActiveTab('Sell')} active={activeTab === 'Sell'}>
Sell
</Tab>
</TabContainer>
<Tabs activeTab={activeTab} setActiveTab={setActiveTab} />

<DropdownContainer>
<DropdownHeader => setIsOpen(!isOpen)}>
<img
src={`/images/${network === Networks.ETHEREUM ? 'ethereum' : 'gnosis-chain'}.svg`}
alt={network ? NETWORK_MAP[network] : ''}
/>
<b>{network ? NETWORK_MAP[network] : ''}</b>
<Image src={networkImage} alt={networkName} width={16} height={16} />
<b>{networkName}</b>
</DropdownHeader>
{isOpen && (
<DropdownBody>
{platforms?.ethereum?.contractAddress && (
<DropdownOption => handleSelect('ethereum')}>
<img src="/images/ethereum.svg" alt="Ethereum" />
Ethereum
</DropdownOption>
)}
{platforms?.xdai?.contractAddress && (
<DropdownOption => handleSelect('xdai')}>
<img src="/images/gnosis-chain.svg" alt="Gnosis Chain" />
Gnosis Chain
</DropdownOption>
)}
</DropdownBody>
)}
{isOpen && getDropdownBody(platforms, handleSelect)}
</DropdownContainer>

<InputLabel>
{activeTab === 'Buy' ? 'Receive' : 'Send'}

<div>
<TokenLabel>
<img src={tokenImage} alt={tokenSymbol} />
<Image src={tokenImage} alt={tokenSymbol} width={20} height={20} />
<span>{tokenSymbol}</span>
</TokenLabel>
<Input min={0} value={amount} type="text" placeholder="0" />
Expand All @@ -326,7 +358,7 @@ export const SwapWidget = ({ tokenId, tokenSymbol, tokenImage, platforms }: Swap
...CONFIG.utm,
utmContent: 'utm_content=swap-widget-token__' + encodeURI(tokenId),
}}
href={onSwap()}
href={getSwapUrl()}
passHref
>
<Button label={`Swap ${tokenSymbol}`} fontSize={1.6} minHeight={4.2} />
Expand Down
27 changes: 27 additions & 0 deletions apps/cow-fi/const/networkMap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@ export const NETWORK_MAP = {
xdai: 'Gnosis Chain',
}

export const NETWORK_ID_MAP: Record<Network, number> = {
ethereum: 1,
base: 8453,
'arbitrum-one': 42161,
avalanche: 43114,
'polygon-pos': 137,
xdai: 100,
}

export const NETWORK_IMAGE_MAP: Record<Network, string> = {
ethereum: '/images/ethereum.svg',
base: '/images/base-chain.svg',
Expand All @@ -25,4 +34,22 @@ export const NETWORK_URL_MAP: Record<Network, string> = {
xdai: 'https://gnosisscan.io/address/',
}

export const NETWORK_DEFAULT_SELL_TOKEN_MAP: Record<Network, string> = {
ethereum: 'WETH',
base: 'WETH',
'arbitrum-one': 'WETH',
avalanche: 'WAVAX',
'polygon-pos': 'WPOL',
xdai: 'WXDAI',
}

export const NETWORK_DEFAULT_BUY_TOKEN_MAP: Record<Network, string> = {
ethereum: 'WETH',
base: 'WETH',
'arbitrum-one': 'WETH',
avalanche: 'WAVAX',
'polygon-pos': 'WPOL',
xdai: 'WXDAI',
}

export type Network = keyof typeof NETWORK_MAP
Loading
0