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: full wire-up #44

Merged
merged 99 commits into from
Feb 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
99 commits
Select commit Hold shift + click to select a range
94be8bc
feat: chainflip assets only
gomesalexandre Feb 4, 2025
9859c8b
fix: close
gomesalexandre Feb 4, 2025
d10aaaa
feat: progression
gomesalexandre Feb 4, 2025
7385e97
feat: progression
gomesalexandre Feb 4, 2025
013d44b
feat: progression
gomesalexandre Feb 4, 2025
792e2f0
feat: checkpoint
gomesalexandre Feb 4, 2025
dadac55
feat: more proregression
gomesalexandre Feb 4, 2025
48c25b6
feat: cleanup
gomesalexandre Feb 4, 2025
bcc996c
feat: checkpoint
gomesalexandre Feb 4, 2025
59a0b8f
feat: checkpoint
gomesalexandre Feb 4, 2025
a945157
feat: latest react-router
gomesalexandre Feb 5, 2025
4f6594d
feat: switch assets
gomesalexandre Feb 5, 2025
a0a3f8e
feat: prgoression or whatever it's spelled
gomesalexandre Feb 5, 2025
d3d81cf
feat: progression
gomesalexandre Feb 5, 2025
b7826c0
feat: bye bye
gomesalexandre Feb 5, 2025
7bbf9b1
fix: shit
gomesalexandre Feb 5, 2025
716edd4
feat: animations
gomesalexandre Feb 5, 2025
a151dbe
fix: to asset
gomesalexandre Feb 5, 2025
411d787
fix: status endpoint
gomesalexandre Feb 5, 2025
265bdd5
feat: checkpoint
gomesalexandre Feb 5, 2025
d7e8019
chore: naming
gomesalexandre Feb 5, 2025
3411fec
feat: check
gomesalexandre Feb 5, 2025
ff75ece
feat: progression
gomesalexandre Feb 5, 2025
9932729
feat: ocd order
gomesalexandre Feb 5, 2025
8dc14cf
feat: progression
gomesalexandre Feb 5, 2025
076ea2c
feat: meta tag
gomesalexandre Feb 5, 2025
ecb1f83
feat: monkey patch isCompleted
gomesalexandre Feb 5, 2025
23ade28
feat: progress
gomesalexandre Feb 5, 2025
85b072a
feat: ion
gomesalexandre Feb 5, 2025
8c6d705
feat: rm monkey
gomesalexandre Feb 5, 2025
d88b8b2
feat: progress
gomesalexandre Feb 5, 2025
8d78f1a
wip: wip
gomesalexandre Feb 5, 2025
ce2456e
fix: lint ish
gomesalexandre Feb 5, 2025
836630b
fix: more lint
gomesalexandre Feb 6, 2025
aa64c6c
feat: react-number-format
gomesalexandre Feb 6, 2025
f2587bc
fix: lint
gomesalexandre Feb 6, 2025
9dcf281
feat: cleanup
gomesalexandre Feb 6, 2025
e9fda92
feat: cleanup
gomesalexandre Feb 6, 2025
0e8bdc6
feat: checkpoint cleanup
gomesalexandre Feb 6, 2025
0b629c4
feat: cleanup pair select
gomesalexandre Feb 6, 2025
c1d0292
feat: cleanup asset select
gomesalexandre Feb 6, 2025
3dc56b2
feat: types
gomesalexandre Feb 6, 2025
aa40bc4
fix: types
gomesalexandre Feb 6, 2025
cc12437
fix: lint
gomesalexandre Feb 6, 2025
1645921
feat: improve types
gomesalexandre Feb 6, 2025
ea92f01
feat: types
gomesalexandre Feb 6, 2025
d6834e6
fix: shit
gomesalexandre Feb 6, 2025
1075b6b
feat: wip
gomesalexandre Feb 6, 2025
37e66f3
feat: improve flow
gomesalexandre Feb 6, 2025
6f9c3b6
feat: more pretty
gomesalexandre Feb 6, 2025
919850e
feat: more clean more up
gomesalexandre Feb 6, 2025
d27497e
fix: bps
gomesalexandre Feb 6, 2025
5c1e57d
feat: surface fees
gomesalexandre Feb 6, 2025
2a3984f
feat: eta
gomesalexandre Feb 6, 2025
3d2b328
feat: gm
gomesalexandre Feb 6, 2025
82212c8
fix: ci
gomesalexandre Feb 6, 2025
ebec4c8
feat: default pair
gomesalexandre Feb 6, 2025
88b2b08
feat: error-handling
gomesalexandre Feb 6, 2025
7fff44d
fix: styles
gomesalexandre Feb 6, 2025
5f9fb54
fix: shit
gomesalexandre Feb 6, 2025
8246a69
feat: animated
gomesalexandre Feb 6, 2025
47e3df5
feat: handle progression
gomesalexandre Feb 6, 2025
492d860
feat: handle refund
gomesalexandre Feb 6, 2025
1a4060a
fix: stuff
gomesalexandre Feb 6, 2025
c14e373
fix: more shit
gomesalexandre Feb 6, 2025
d21d868
fix: more more shit
gomesalexandre Feb 6, 2025
7b09afd
feat: what if I told you this fixes shit
gomesalexandre Feb 6, 2025
b100bee
fix: lint
gomesalexandre Feb 6, 2025
557c138
feat: add back deposit channel link
gomesalexandre Feb 7, 2025
76e6d51
feat: transitions
gomesalexandre Feb 7, 2025
952b16f
feat: open modal on click
gomesalexandre Feb 7, 2025
7011baf
chore: 1 percent
gomesalexandre Feb 7, 2025
dec886a
feat: ui nitpicks
gomesalexandre Feb 7, 2025
e81467c
feat: she bakk
gomesalexandre Feb 7, 2025
da860e0
feat: add estimated receive
gomesalexandre Feb 7, 2025
6f74016
feat: we gucci
gomesalexandre Feb 7, 2025
1a88573
feat: lfg
gomesalexandre Feb 7, 2025
3f8f504
fix: lint
gomesalexandre Feb 7, 2025
1fe7ca5
feat: cleanup
gomesalexandre Feb 7, 2025
b000fcb
feat: debounce
gomesalexandre Feb 7, 2025
b977e42
fix: lint
gomesalexandre Feb 7, 2025
ffa4ab3
feat: cleanup
gomesalexandre Feb 7, 2025
5a5a59f
feat: cleanup
gomesalexandre Feb 7, 2025
64ece4d
feat: more cleanup
gomesalexandre Feb 7, 2025
8df4cc1
feat: more more cleanup
gomesalexandre Feb 7, 2025
e4984f7
feat: final cleanup
gomesalexandre Feb 7, 2025
03c6d5d
feat: use caip types
gomesalexandre Feb 10, 2025
1b9fc08
feat: vernacular
gomesalexandre Feb 10, 2025
40fd051
feat: more vernacular
gomesalexandre Feb 10, 2025
2956cc1
ocd: lambda shorthand
gomesalexandre Feb 10, 2025
9ebf387
feat: comission bps env var
gomesalexandre Feb 10, 2025
c747ea5
feat: explorer base url env var
gomesalexandre Feb 10, 2025
c45f23f
feat: actually overfetch
gomesalexandre Feb 10, 2025
1ee6e8d
feat: disable form on invalid
gomesalexandre Feb 10, 2025
64db3a8
feat: consistently revalidate/lower debounce
gomesalexandre Feb 10, 2025
18f27bd
fix: switch assets
gomesalexandre Feb 10, 2025
a915749
feat: rm market-data bits
gomesalexandre Feb 10, 2025
0256fcf
feat: animatooor
gomesalexandre Feb 10, 2025
8c0a9fd
fix: shapeshift fee should be broker fee
gomesalexandre Feb 10, 2025
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
3 changes: 3 additions & 0 deletions .env
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,6 @@ VITE_CHATWOOT_URL=https://app.chatwoot.com

VITE_CHAINFLIP_API_KEY=09bc0796ff40435482c0a54fa6ae2784
VITE_CHAINFLIP_API_URL=https://chainflip-broker.io
VITE_CHAINFLIP_EXPLORER_BASE_URL=https://scan.chainflip.io

VITE_CHAINFLIP_COMMISSION_BPS=49
3 changes: 1 addition & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ on:
jobs:
install-and-cache:
name: Install Lint Type-check
container: node:18-bullseye
container: node:20-bullseye
runs-on: ubuntu-latest
steps:
- name: Checkout
Expand All @@ -22,7 +22,6 @@ jobs:
- name: Setup Node
uses: actions/setup-node@v3
with:
node-version: '18.20.4'
node-version-file: '.nvmrc'
cache: 'yarn'

Expand Down
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
18
20
2 changes: 1 addition & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React + TS</title>
<title>ShapeShift Classic</title>
</head>
<body>
<div id="root"></div>
Expand Down
16 changes: 14 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,22 +19,34 @@
"@lukemorales/query-key-factory": "^1.3.4",
"@shapeshiftoss/caip": "^8.15.0",
"@shapeshiftoss/types": "^8.6.0",
"@solana/web3.js": "^1.98.0",
"@tanstack/react-query": "^5.65.1",
"@types/qrcode": "^1.5.5",
"axios": "^1.6.5",
"framer-motion": "^10.17.9",
"bech32": "^2.0.0",
"bignumber.js": "^9.1.2",
"dayjs": "^1.11.13",
"framer-motion": "^12.3.0",
"lodash": "^4.17.21",
"match-sorter": "^6.3.1",
"mixpanel-browser": "^2.48.1",
"multicoin-address-validator": "^0.5.24",
"qrcode": "^1.5.4",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-hook-form": "^7.54.2",
"react-icons": "^4.12.0",
"react-router-dom": "^6.21.1",
"react-number-format": "^5.4.3",
"react-router": "^7.1.5",
"react-virtuoso": "^4.6.2",
"viem": "^2.0.3",
"zustand": "^5.0.3"
},
"devDependencies": {
"@types/inquirer": "^9.0.7",
"@types/lodash": "^4",
"@types/mixpanel-browser": "^2.48.1",
"@types/multicoin-address-validator": "^0",
"@types/node": "^20.10.7",
"@types/react": "^18.2.43",
"@types/react-dom": "^18.2.17",
Expand Down
34 changes: 6 additions & 28 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,39 +1,17 @@
import './App.css'

import { Center } from '@chakra-ui/react'
import { QueryClientProvider } from '@tanstack/react-query'
import { createBrowserRouter, RouterProvider } from 'react-router-dom'
import { ChatwootButton } from 'components/Chatwoot'
import { SelectPair } from 'components/SelectPair'
import { Status } from 'components/Status/Status'
import { TradeInput } from 'components/TradeInput'
import { AppRouter } from 'AppRouter'
import { BrowserRouter } from 'react-router'

import { queryClient } from './config/react-query'

const router = createBrowserRouter([
{
path: '/',
element: <SelectPair />,
},
{
path: '/input',
element: <TradeInput />,
},
{
path: '/status',
element: <Status />,
},
])

function App() {
console.log(import.meta.env.VITE_FOO)

const App = () => {
return (
<QueryClientProvider client={queryClient}>
<Center width='full' height='100vh' flexDir='column'>
<RouterProvider router={router} />
<ChatwootButton />
</Center>
<BrowserRouter>
<AppRouter />
</BrowserRouter>
</QueryClientProvider>
)
}
Expand Down
126 changes: 126 additions & 0 deletions src/AppRouter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import { Center } from '@chakra-ui/react'
import { btcAssetId, ethAssetId } from 'constants/caip'
import { AnimatePresence, motion } from 'framer-motion'
import { useEffect, useMemo } from 'react'
import { FormProvider, useForm, useWatch } from 'react-hook-form'
import { Route, Routes, useLocation, useSearchParams } from 'react-router'
import { ChatwootButton } from 'components/Chatwoot'
import { SelectPair } from 'components/SelectPair'
import { Status } from 'components/Status/Status'
import { TradeInput } from 'components/TradeInput'
import type { SwapFormData } from 'types/form'

const selectPair = <SelectPair />
const tradeInput = <TradeInput />
const status = <Status />

const slideVariants = {
initial: { opacity: 0, x: 20 },
animate: { opacity: 1, x: 0 },
exit: { opacity: 0, x: -20 },
}

const fadeUpVariants = {
initial: { opacity: 0, y: 20 },
animate: { opacity: 1, y: 0 },
exit: { opacity: 0, y: -20 },
}

const motionWrapperSx = {
width: '100%',
maxWidth: '450px',
display: 'flex',
justifyContent: 'center',
}

const transition = {
type: 'tween',
ease: [0.25, 0.1, 0.25, 1],
duration: 0.3,
}

const getVariants = (pathname: string) => {
// Not sure what this does but this looks good on status screen so
if (pathname === '/status') return fadeUpVariants
return slideVariants
}

export const AppRouter = () => {
const [searchParams, setSearchParams] = useSearchParams()
const location = useLocation()

const defaultValues = useMemo(
() => ({
sellAssetId: searchParams.get('sellAssetId') || ethAssetId,
buyAssetId: searchParams.get('buyAssetId') || btcAssetId,
sellAmountCryptoBaseUnit: searchParams.get('sellAmountCryptoBaseUnit') || undefined,
destinationAddress: searchParams.get('destinationAddress') || undefined,
refundAddress: searchParams.get('refundAddress') || undefined,
gomesalexandre marked this conversation as resolved.
Show resolved Hide resolved
}),
[searchParams],
)

const methods = useForm<SwapFormData>({
mode: 'all',
reValidateMode: 'onChange',
defaultValues,
})

const formValues = useWatch({ control: methods.control })

// Synchronize queryparams with form
useEffect(() => {
const params = new URLSearchParams(searchParams)

Object.entries(formValues || {}).forEach(([key, value]) => {
if (value) {
params.set(key, String(value))
} else {
params.delete(key)
}
})

setSearchParams(params, { replace: true })
}, [formValues, setSearchParams, searchParams])
gomesalexandre marked this conversation as resolved.
Show resolved Hide resolved

// Synchronize form with queryparams too
useEffect(() => {
const currentParams = Object.fromEntries(searchParams.entries())
const hasParams = Object.keys(currentParams).length > 0

if (hasParams) {
const formData: Partial<SwapFormData> = {}
searchParams.forEach((value, key) => {
if (key in defaultValues) {
formData[key as keyof SwapFormData] = value
}
})
methods.reset(formData)
}
}, [searchParams, methods, defaultValues])

return (
<FormProvider {...methods}>
<Center width='full' height='100vh' flexDir='column'>
<AnimatePresence mode='wait' initial={false}>
<motion.div
key={location.pathname}
initial='initial'
animate='animate'
exit='exit'
variants={getVariants(location.pathname)}
transition={transition}
style={motionWrapperSx}
>
<Routes location={location}>
<Route path='/' element={selectPair} />
<Route path='/input' element={tradeInput} />
<Route path='/status' element={status} />
</Routes>
</motion.div>
</AnimatePresence>
<ChatwootButton />
</Center>
</FormProvider>
)
}
12 changes: 4 additions & 8 deletions src/components/AssetSelectModal/AssetSelectModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@ import {
import type { ChainId } from '@shapeshiftoss/caip'
import type { ChangeEvent } from 'react'
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import AssetData from 'lib/generatedAssetData.json'
import { isNft } from 'lib/utils'
import { useAllAssets } from 'store/assets'
import type { Asset } from 'types/Asset'

import { AssetList } from './AssetList'
Expand All @@ -35,16 +34,13 @@ type NetworkItem = {

export const AssetSelectModal: React.FC<AssetSelectModalProps> = ({ isOpen, onClose, onClick }) => {
const [searchQuery, setSearchQuery] = useState('')
const assets = useMemo(() => Object.values(AssetData) as Asset[], [])
const assets = useAllAssets()
const [activeChain, setActiveChain] = useState<ChainId | 'All'>('All')
const [searchTermAssets, setSearchTermAssets] = useState<Asset[]>([])
const iniitalRef = useRef(null)

const filteredAssets = useMemo(
() =>
activeChain === 'All'
? assets.filter(a => !isNft(a.assetId))
: assets.filter(a => a.chainId === activeChain && !isNft(a.assetId)),
() => (activeChain === 'All' ? assets : assets.filter(a => a.chainId === activeChain)),
[activeChain, assets],
)

Expand Down Expand Up @@ -136,7 +132,7 @@ export const AssetSelectModal: React.FC<AssetSelectModalProps> = ({ isOpen, onCl
<Text fontWeight='bold' fontSize='md'>
Select asset
</Text>
<CloseButton position='relative' />
<CloseButton position='relative' onClick={handleClose} />
</Flex>
<Input
size='lg'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,9 @@
import { fromAssetId } from '@shapeshiftoss/caip'
import { matchSorter } from 'match-sorter'
import { isEthAddress } from 'lib/utils'
import type { Asset } from 'types/Asset'

export const filterAssetsBySearchTerm = (search: string, assets: Asset[]) => {
if (!assets) return []

const searchLower = search.toLowerCase()

if (isEthAddress(search)) {
return assets.filter(
asset => fromAssetId(asset?.assetId).assetReference.toLowerCase() === searchLower,
)
}

return matchSorter(assets, search, {
keys: ['name', 'symbol'],
threshold: matchSorter.rankings.CONTAINS,
Expand Down
16 changes: 10 additions & 6 deletions src/components/AssetSelection.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
import { Avatar, Button, Text } from '@chakra-ui/react'
import type { Asset } from 'types/Asset'
import type { AssetId } from '@shapeshiftoss/caip'
import { memo } from 'react'
import { useAssetById } from 'store/assets'

type AssetSelectionProps = {
onClick: () => void
label: string
asset?: Asset
assetId?: AssetId
}

export const AssetSelection: React.FC<AssetSelectionProps> = ({ label, onClick, asset }) => {
export const AssetSelection = memo(({ label, onClick, assetId }: AssetSelectionProps) => {
const asset = useAssetById(assetId)

return (
<Button flexDir='column' height='auto' py={4} gap={4} flex={1} onClick={onClick}>
<Text color='text.subtle'>{label}</Text>
<Avatar src={asset ? asset.icon : ''} />
<Avatar src={asset?.icon || ''} />
<Text textOverflow='ellipsis' overflow='hidden' width='full'>
{asset ? asset.name : 'Select Asset'}
{asset?.name || 'Select Asset'}
</Text>
</Button>
)
}
})
44 changes: 44 additions & 0 deletions src/components/QRCode/QRCode.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { Box, Center } from '@chakra-ui/react'
import qrcode from 'qrcode'
import { useEffect, useRef } from 'react'

type QRCodeProps = {
content: string
width: number
icon?: React.ReactNode
standardColors?: boolean
}

export const QRCode: React.FC<QRCodeProps> = ({ content, width, icon, standardColors = false }) => {
const ref = useRef<HTMLCanvasElement | null>(null)

useEffect(() => {
if (ref.current && width) {
qrcode.toCanvas(ref.current, content, {
margin: 1,
width,
color: standardColors ? undefined : { light: '#ffffff00', dark: '#161616ff' },
})
}
}, [content, width, standardColors])

return (
<Box position='relative'>
{icon && (
<Center
position='absolute'
top='50%'
left='50%'
transform='translate(-50%, -50%)'
boxSize='34px'
borderRadius='md'
bg='background.surface.raised.base'
zIndex={1}
>
{icon}
</Center>
)}
<canvas ref={ref} style={{ width: '100%' }} />
</Box>
)
}
Loading