(false)
+
+const CloseButton = () => {
+ const setTxHashAtom = useSetAtom(txHashAtom)
+ const setSuccess = useSetAtom(successAtom)
+ const setUserInputAtom = useSetAtom(userInputAtom)
+ const setRedeemAssets = useSetAtom(redeemAssetsAtom)
+ const setCowswapOrderIdsAtom = useSetAtom(cowswapOrderIdsAtom)
+ const setCowswapOrdersCreatedAtAtom = useSetAtom(cowswapOrdersCreatedAtAtom)
+ const setCowswapOrdersAtom = useSetAtom(cowswapOrdersAtom)
+ const setQuotesAtom = useSetAtom(quotesAtom)
+ const setUniversalSuccessOrdersAtom = useSetAtom(universalSuccessOrdersAtom)
+ const setFallbackQuotesAtom = useSetAtom(fallbackQuotesAtom)
+
+ const handleClose = () => {
+ setTxHashAtom(undefined)
+ setSuccess(false)
+ setUserInputAtom('')
+ setRedeemAssets({})
+ setQuotesAtom({})
+ setCowswapOrderIdsAtom([])
+ setCowswapOrdersCreatedAtAtom(undefined)
+ setCowswapOrdersAtom([])
+ setUniversalSuccessOrdersAtom([])
+ setFallbackQuotesAtom({})
+ }
+
+ return (
+
+ )
+}
+
+const SuccessHeader = () => {
+ const chainId = useAtomValue(chainIdAtom)
+ const indexDTF = useAtomValue(indexDTFAtom)
+ const basket = useAtomValue(indexDTFBasketAtom)
+ const setViewTransactions = useSetAtom(viewTransactionsAtom)
+
+ return (
+
+
+
+
+
+
+
+
+
+ )
+}
+
+const DTFAmount = () => {
+ const chainId = useAtomValue(chainIdAtom)
+ const indexDTF = useAtomValue(indexDTFAtom)
+ const indexDTFPrice = useAtomValue(indexDTFPriceAtom)
+ const balanceDifference = useAtomValue(balanceDifferenceAtom)
+ const operation = useAtomValue(operationAtom)
+ const sharesRedeemed = useAtomValue(userInputAtom)
+ const mintValue = useAtomValue(mintValueAtom)
+ const valueMinted = (indexDTFPrice || 0) * mintValue
+ const valueRedeemed = (indexDTFPrice || 0) * Number(sharesRedeemed)
+
+ const priceImpact = balanceDifference
+ ? (valueMinted * 100) / balanceDifference - 100
+ : 0
+
+ return (
+
+
+ {operation === 'mint' ? 'You Minted:' : 'You Redeemed:'}
+
+
+
+
+ {operation === 'mint'
+ ? formatTokenAmount(mintValue)
+ : formatTokenAmount(Number(sharesRedeemed))}
+
+
{indexDTF?.token.symbol || ''}
+
+
+
+
+ ${formatCurrency(operation === 'mint' ? valueMinted : valueRedeemed)}{' '}
+ {operation === 'mint' && (
+
+ ({formatPercentage(priceImpact)})
+
+ )}
+
+
+ )
+}
+
+const USDCAmount = () => {
+ const inputAmount = useAtomValue(userInputAtom)
+ const operation = useAtomValue(operationAtom)
+ const balanceDifference = useAtomValue(balanceDifferenceAtom)
+ const indexDTFPrice = useAtomValue(indexDTFPriceAtom)
+ const valueRedeemed = (indexDTFPrice || 0) * Number(inputAmount)
+ const priceImpact = valueRedeemed
+ ? (balanceDifference * 100) / valueRedeemed - 100
+ : 0
+
+ return (
+
+
+ {operation === 'mint' ? 'You Used:' : 'You Received:'}
+
+
+
+
+ {formatCurrency(balanceDifference)}
+
+
USDC
+
+ {operation === 'mint' && (
+
+
+ {formatCurrency(Number(inputAmount))} USDC
+
+
+
+ )}
+
+
+ ${formatCurrency(balanceDifference)}
+ {operation === 'mint' && (
+
+ ${formatCurrency(Number(inputAmount))}
+
+ )}
+ {operation === 'redeem' && (
+
+ ({priceImpact > 0 ? '+' : ''}
+ {formatPercentage(priceImpact)})
+
+ )}
+
+ {operation === 'mint' && (
+
+
+
+
+ )}
+
+ )
+}
+
+const BufferInfo = () => {
+ const savedAmount = useAtomValue(savedAmountAtom)
+
+ return (
+
+
+
+ $
+ {formatCurrency(savedAmount)}
+
+
+ )
+}
+
+const MainTransaction = () => {
+ const indexDTF = useAtomValue(indexDTFAtom)
+ const txHash = useAtomValue(txHashAtom)
+ const operation = useAtomValue(operationAtom)
+
+ return (
+
+
+
+
+
+ {operation === 'mint'
+ ? `${indexDTF?.token.symbol} Minted`
+ : `${indexDTF?.token.symbol} Redeemed`}
+
+
+ {shortenAddress(indexDTF?.id || '')}
+
+
+
+
+
+ )
+}
+
+const Transactions = () => {
+ const setViewTransactions = useSetAtom(viewTransactionsAtom)
+ const cowswapOrders = useAtomValue(cowswapOrdersAtom)
+ const universalSuccessOrders = useAtomValue(universalSuccessOrdersAtom)
+
+ return (
+
+
+
+
+ All Transactions
+
+
+
+
+
+
+ {cowswapOrders.map(({ orderId }, index) => (
+
+ ))}
+ {universalSuccessOrders.map((order, index) => (
+
+ ))}
+
+
+
+ )
+}
+
+const Success = () => {
+ const viewTransactions = useAtomValue(viewTransactionsAtom)
+ const [showConfetti, setShowConfetti] = useState(true)
+
+ useEffect(() => {
+ const timer = setTimeout(() => {
+ setShowConfetti(false)
+ }, 2000)
+
+ return () => clearTimeout(timer)
+ }, [])
+
+ if (viewTransactions) {
+ return
+ }
+
+ return (
+
+ {showConfetti && (
+
+ )}
+
+
+ )
+}
+
+export default Success
diff --git a/src/views/index-dtf/issuance/async-swaps/types.ts b/src/views/index-dtf/issuance/async-swaps/types.ts
new file mode 100644
index 000000000..ef29b296a
--- /dev/null
+++ b/src/views/index-dtf/issuance/async-swaps/types.ts
@@ -0,0 +1,40 @@
+import { Token } from '@/types'
+import { EnrichedOrder, OrderQuoteResponse } from '@cowprotocol/cow-sdk'
+import { Quote } from 'universal-sdk'
+import { CustomUniversalQuote } from './providers/universal'
+
+export enum QuoteProvider {
+ CowSwap = 'CowSwap',
+ Universal = 'Universal',
+}
+
+export type CowswapQuote = {
+ token: Token
+ success: true
+ type: QuoteProvider.CowSwap
+ data: OrderQuoteResponse
+}
+
+export type UniversalQuote = {
+ token: Token
+ success: true
+ type: QuoteProvider.Universal
+ data: CustomUniversalQuote
+}
+
+export type QuoteAggregated =
+ | {
+ token: Token
+ success: false
+ }
+ | CowswapQuote
+ | UniversalQuote
+
+export type UniversalOrder = Quote & {
+ orderId: string
+ transactionHash: string
+}
+
+export type AsyncSwapOrderResponse = {
+ cowswapOrders: (EnrichedOrder & { orderId: string })[]
+}
diff --git a/src/views/index-dtf/issuance/async-swaps/universal-order.tsx b/src/views/index-dtf/issuance/async-swaps/universal-order.tsx
new file mode 100644
index 000000000..0078d2c13
--- /dev/null
+++ b/src/views/index-dtf/issuance/async-swaps/universal-order.tsx
@@ -0,0 +1,96 @@
+import TokenLogo from '@/components/token-logo'
+import { Skeleton } from '@/components/ui/skeleton'
+import { chainIdAtom } from '@/state/atoms'
+import { indexDTFBasketAtom } from '@/state/dtf/atoms'
+import { formatCurrency, formatTokenAmount } from '@/utils'
+import { ExplorerDataType, getExplorerLink } from '@/utils/getExplorerLink'
+import { useAtomValue } from 'jotai'
+import { ArrowUpRight, Check } from 'lucide-react'
+import { useMemo } from 'react'
+import { Link } from 'react-router-dom'
+import { formatUnits } from 'viem'
+import { operationAtom } from './atom'
+import { UniversalOrder as UniversalOrderType } from './types'
+import { getUniversalTokenAddress } from './providers/universal'
+
+const UniversalOrder = ({ order }: { order: UniversalOrderType }) => {
+ const operation = useAtomValue(operationAtom)
+ const indexDTFBasket = useAtomValue(indexDTFBasketAtom)
+ const chainId = useAtomValue(chainIdAtom)
+ const { token, firstAmount, secondAmount } = useMemo(() => {
+ const token = getUniversalTokenAddress(order?.token)
+ return operation === 'redeem'
+ ? {
+ token,
+ firstAmount: order?.token_amount,
+ secondAmount: order?.pair_token_amount,
+ }
+ : {
+ token,
+ firstAmount: order?.token_amount,
+ secondAmount: order?.pair_token_amount,
+ }
+ }, [order, operation])
+
+ return (
+
+
+
t.address === token)?.symbol || ''
+ }
+ size="xl"
+ />
+
+ {secondAmount ? (
+
+ {operation === 'mint' ? '-' : '+'}{' '}
+ {formatCurrency(Number(formatUnits(BigInt(secondAmount), 6)))}{' '}
+ USDC
+
+ ) : (
+
+ )}
+ {firstAmount ? (
+
+ {operation === 'mint' ? '+' : '-'}{' '}
+ {formatTokenAmount(
+ Number(
+ formatUnits(
+ BigInt(firstAmount),
+ indexDTFBasket?.find((t) => t.address === token)
+ ?.decimals || 18
+ )
+ )
+ )}{' '}
+ {indexDTFBasket?.find((t) => t.address === token)?.symbol || ''}
+
+ ) : (
+
+ )}
+
+
+
+
+ )
+}
+
+export default UniversalOrder
diff --git a/src/views/index-dtf/issuance/manual/index.tsx b/src/views/index-dtf/issuance/manual/index.tsx
index 1ec4b330f..61dc79b61 100644
--- a/src/views/index-dtf/issuance/manual/index.tsx
+++ b/src/views/index-dtf/issuance/manual/index.tsx
@@ -1,8 +1,22 @@
+import { useSetAtom } from 'jotai'
import AssetList from './components/asset-list'
import IndexManualIssuance from './components/index-manual-issuance'
import Updater from './updater'
+import { amountAtom } from './atoms'
+import { useEffect } from 'react'
+import { useSearchParams } from 'react-router-dom'
const IndexDTFManualIssuance = () => {
+ const setAmount = useSetAtom(amountAtom)
+ const [searchParams] = useSearchParams()
+ const amountIn = searchParams.get('amountIn')
+
+ useEffect(() => {
+ if (amountIn) {
+ setAmount(amountIn)
+ }
+ }, [])
+
return (
<>
diff --git a/src/views/index-dtf/issuance/manual/updater.tsx b/src/views/index-dtf/issuance/manual/updater.tsx
index 8ec0515b1..80ad6a995 100644
--- a/src/views/index-dtf/issuance/manual/updater.tsx
+++ b/src/views/index-dtf/issuance/manual/updater.tsx
@@ -11,6 +11,8 @@ import {
assetDistributionAtom,
balanceMapAtom,
} from './atoms'
+import useERC20Balance from '@/hooks/useERC20Balance'
+import { indexDTFBalanceAtom } from '../async-swaps/atom'
const balanceCallsAtom = atom((get) => {
const wallet = get(walletAtom)
@@ -57,6 +59,9 @@ const Updater = () => {
const setAssetDistribution = useSetAtom(assetDistributionAtom)
const setAllowances = useSetAtom(allowanceMapAtom)
const chainId = useAtomValue(chainIdAtom)
+ const setIndexDTFBalance = useSetAtom(indexDTFBalanceAtom)
+
+ const { data: balance } = useERC20Balance(indexDTF?.id)
const { data } = useWatchReadContracts({
contracts: calls,
@@ -110,6 +115,10 @@ const Updater = () => {
},
})
+ useEffect(() => {
+ setIndexDTFBalance(balance || 0n)
+ }, [balance, setIndexDTFBalance])
+
useEffect(() => {
if (data) {
setBalance(data)
diff --git a/tailwind.config.ts b/tailwind.config.ts
index 49e7dd437..d1bce770a 100644
--- a/tailwind.config.ts
+++ b/tailwind.config.ts
@@ -131,6 +131,10 @@ const config = {
transform: 'rotate(360deg)',
},
},
+ shimmer: {
+ '0%': { backgroundPosition: '200% 0' },
+ '100%': { backgroundPosition: '-200% 0' },
+ },
},
animation: {
'accordion-down': 'accordion-down 0.2s ease-out',
@@ -140,6 +144,7 @@ const config = {
'width-expand':
'width-expand 0.4s cubic-bezier(0.25, 0.8, 0.25, 1) forwards',
'spin-slow': 'spin-slow 4s linear infinite',
+ shimmer: 'shimmer 2s linear infinite',
},
},
},
diff --git a/vite.config.ts b/vite.config.ts
index 6ef9e9a3c..ef4ab88bf 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -71,55 +71,55 @@ export default defineConfig({
manualChunks: (id) => {
// Skip non-node_modules
if (!id.includes('node_modules')) {
- return undefined;
+ return undefined
}
// Core React dependencies
if (id.includes('react-dom')) {
- return 'react-dom';
+ return 'react-dom'
}
if (id.includes('react') && !id.includes('react-')) {
- return 'react';
+ return 'react'
}
// Large libraries that should be separate
if (id.includes('@rainbow-me/rainbowkit')) {
- return 'rainbowkit';
+ return 'rainbowkit'
}
if (id.includes('wagmi') || id.includes('@wagmi')) {
- return 'wagmi';
+ return 'wagmi'
}
if (id.includes('viem')) {
- return 'viem';
+ return 'viem'
}
if (id.includes('@walletconnect')) {
- return 'walletconnect';
+ return 'walletconnect'
}
if (id.includes('@coinbase/wallet-sdk')) {
- return 'coinbase';
+ return 'coinbase'
}
// UI libraries
if (id.includes('@radix-ui')) {
- return 'radix-ui';
+ return 'radix-ui'
}
if (id.includes('recharts')) {
- return 'charts';
+ return 'charts'
}
// Other vendor libs
if (id.includes('ethers')) {
- return 'ethers';
+ return 'ethers'
}
if (id.includes('@tanstack')) {
- return 'tanstack';
+ return 'tanstack'
}
if (id.includes('jotai')) {
- return 'jotai';
+ return 'jotai'
}
- }
- }
- }
+ },
+ },
+ },
},
resolve: {
alias: {
@@ -127,6 +127,12 @@ export default defineConfig({
types: path.resolve('src/types/'),
utils: path.resolve('src/utils/'),
'@': path.resolve(__dirname, './src'),
+ '@uniswap/uniswapx-sdk': path.resolve(
+ __dirname,
+ 'node_modules/@uniswap/uniswapx-sdk/dist/src/index.js'
+ ),
+ // Polyfills for node modules - @cowprotocol/cow-sdk needs it
+ 'node-fetch': 'cross-fetch',
},
},
optimizeDeps: {
@@ -139,12 +145,13 @@ export default defineConfig({
'@radix-ui/react-dialog',
'@radix-ui/react-select',
'@radix-ui/react-tabs',
+ '@uniswap/uniswapx-sdk',
],
esbuildOptions: {
target: 'es2020',
// Help with tree shaking
- treeShaking: true
- }
+ treeShaking: true,
+ },
},
server: {
port: 3000,