Skip to content

Commit fc43689

Browse files
authored
Merge pull request #81 from 0xbow-io/release/v1.3.0
Release v1.3.0
2 parents b0bd1a2 + eddf6e8 commit fc43689

File tree

11 files changed

+502
-35
lines changed

11 files changed

+502
-35
lines changed

CHANGELOG.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,33 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [1.3.0] - 2025-07-19
9+
10+
### Added
11+
12+
- DAI pool support
13+
- Withdrawal fees breakdown
14+
15+
## [1.2.0] - 2025-07-18
16+
17+
### Added
18+
19+
- sUSDS pool support
20+
- handling relayer fees processing
21+
22+
### Changed
23+
24+
- changed withdrawal modal steps
25+
26+
## [1.1.0] - 2025-07-16
27+
28+
### Added
29+
30+
- USDS pool support
31+
32+
### Changed
33+
34+
- Relayer quotation logic
835

936
## [1.2.0] - 2025-07-18
1037

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "privacy-pools-website",
33
"private": true,
4-
"version": "1.2.0",
4+
"version": "1.3.0",
55
"type": "module",
66
"license": "MIT",
77
"author": "Wonderland",

src/assets/icons/dai.svg

Lines changed: 13 additions & 0 deletions
Loading

src/components/AssetSelect.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const ALL_TOKEN_OPTIONS: Option[] = [
1515
{ value: 'ETH', label: 'ETH' },
1616
{ value: 'USDS', label: 'USDS' },
1717
{ value: 'sUSDS', label: 'sUSDS' },
18+
{ value: 'DAI', label: 'DAI' },
1819
{ value: 'USDT', label: 'USDT' },
1920
{ value: 'USDC', label: 'USDC' },
2021
];

src/config/chainData.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Address, parseEther, parseUnits } from 'viem';
22
import { Chain, mainnet, sepolia } from 'viem/chains';
33
import { getEnv } from '~/config/env';
4+
import daiIcon from '~/assets/icons/dai.svg';
45
import mainnetIcon from '~/assets/icons/mainnet_color.svg';
56
import susdsIcon from '~/assets/icons/susds.svg';
67
import usdsIcon from '~/assets/icons/usds.svg';
@@ -13,7 +14,7 @@ const testnetChains: readonly [Chain, ...Chain[]] = [sepolia];
1314

1415
export const whitelistedChains = IS_TESTNET ? testnetChains : mainnetChains;
1516

16-
export type ChainAssets = 'ETH' | 'USDS' | 'sUSDS' | 'USDC' | 'USDT';
17+
export type ChainAssets = 'ETH' | 'USDS' | 'sUSDS' | 'DAI' | 'USDC' | 'USDT';
1718

1819
export interface PoolInfo {
1920
chainId: number;
@@ -95,6 +96,18 @@ const mainnetChainData: ChainData = {
9596
assetDecimals: 18,
9697
icon: susdsIcon.src,
9798
},
99+
{
100+
chainId: mainnet.id,
101+
address: '0x1c31C03B8CB2EE674D0F11De77135536db828257',
102+
assetAddress: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
103+
scope: 15036211945525489305347805074288289358577232744970551616130812771908439733411n,
104+
deploymentBlock: 22946646n,
105+
entryPointAddress: '0x6818809EefCe719E480a7526D76bD3e561526b46',
106+
maxDeposit: parseUnits('1000000', 18),
107+
asset: 'DAI',
108+
assetDecimals: 18,
109+
icon: daiIcon.src,
110+
},
98111
],
99112
},
100113
};

src/containers/Modals/Review/DataSection.tsx

Lines changed: 110 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
'use client';
2-
import { useEffect } from 'react';
3-
import { Stack, styled, Typography } from '@mui/material';
2+
import { useEffect, useState } from 'react';
3+
import { ExpandMore as ExpandMoreIcon } from '@mui/icons-material';
4+
import { Stack, styled, Typography, IconButton, Collapse } from '@mui/material';
45
import { formatUnits, parseUnits } from 'viem';
56
import { useAccount } from 'wagmi';
67
import { ExtendedTooltip as Tooltip } from '~/components';
8+
import { useQuoteContext } from '~/contexts/QuoteContext';
79
import {
810
useExternalServices,
911
usePoolAccountsContext,
@@ -13,9 +15,12 @@ import {
1315
} from '~/hooks';
1416
import { EventType } from '~/types';
1517
import { getUsdBalance, truncateAddress } from '~/utils';
18+
import { FeeBreakdown } from './FeeBreakdown';
1619

1720
export const DataSection = () => {
1821
const { address } = useAccount();
22+
const [isFeeBreakdownOpen, setIsFeeBreakdownOpen] = useState(false);
23+
const { quoteState } = useQuoteContext();
1924
const {
2025
balanceBN: { symbol, decimals },
2126
price,
@@ -44,6 +49,8 @@ export const DataSection = () => {
4449
isQuoteValid,
4550
isExpired,
4651
feeBPS: quoteFeesBPS,
52+
baseFeeBPS: quoteBaseFeeBPS,
53+
extraGasAmountETH: quoteExtraGasAmountETH,
4754
quoteCommitment,
4855
} = useRequestQuote({
4956
getQuote: getQuote || (() => Promise.reject(new Error('No relayer data'))),
@@ -80,9 +87,6 @@ export const DataSection = () => {
8087
const relayerFees = (BigInt(effectiveFeeBPS) * parseUnits(amount, decimals)) / 100n / 100n;
8188

8289
const fees = isDeposit ? aspDataFees : relayerFees;
83-
const feeFormatted = formatUnits(fees, decimals);
84-
const feeUSD = getUsdBalance(price, feeFormatted, decimals);
85-
const feeText = `${feeFormatted} ${symbol} (~ ${feeUSD} USD)`;
8690

8791
// Create full precision tooltips - show complete decimal precision
8892
const formatFullPrecision = (value: bigint, decimals: number) => {
@@ -92,25 +96,53 @@ export const DataSection = () => {
9296
}
9397
const integerPart = valueStr.slice(0, -decimals);
9498
const decimalPart = valueStr.slice(-decimals);
95-
return `${integerPart}.${decimalPart}`;
96-
};
99+
const result = `${integerPart}.${decimalPart}`;
100+
101+
// Remove trailing zeros, but keep at least 2 decimal places
102+
const trimmed = result.replace(/\.?0+$/, '');
103+
if (!trimmed.includes('.')) {
104+
return `${trimmed}.00`;
105+
}
106+
const decimalIndex = trimmed.indexOf('.');
107+
const currentDecimals = trimmed.length - decimalIndex - 1;
108+
if (currentDecimals < 2) {
109+
return trimmed + '0'.repeat(2 - currentDecimals);
110+
}
97111

98-
const feeTooltip = `${formatFullPrecision(fees, decimals)} ${symbol}`;
112+
return trimmed;
113+
};
99114

100115
const feesCollectorAddress = isDeposit
101116
? selectedPoolInfo.entryPointAddress
102117
: currentSelectedRelayerData?.relayerAddress;
103118
const feesCollector = `OxBow (${truncateAddress(feesCollectorAddress)})`;
104119

105120
const amountUSD = getUsdBalance(price, amount, decimals);
121+
122+
// Value is now the actual amount being withdrawn (amount minus fees)
106123
const amountWithFeeBN = parseUnits(amount, decimals) - fees;
107124
const amountWithFee = formatUnits(amountWithFeeBN, decimals);
108125
const amountWithFeeUSD = getUsdBalance(price, amountWithFee, decimals);
109-
110-
const valueText = `${amountWithFee} ${symbol} (~ ${amountWithFeeUSD} USD)`;
126+
const valueText = `${parseFloat(amountWithFee).toString()} ${symbol} (~$${parseFloat(amountWithFeeUSD.replace('$', '')).toFixed(2)} USD)`;
111127
const valueTooltip = `${formatFullPrecision(amountWithFeeBN, decimals)} ${symbol}`;
112128

113-
const totalText = `~${amount.slice(0, 6)} ${symbol} (~ ${amountUSD} USD)`;
129+
// Net Fee calculation (includes extra gas amount if enabled)
130+
let netFeeAmount = fees;
131+
if (quoteState.extraGas && quoteExtraGasAmountETH) {
132+
// Convert extraGasAmountETH from wei to token amount
133+
const extraGasETH = parseFloat(formatUnits(BigInt(quoteExtraGasAmountETH), 18));
134+
const extraGasInToken = (extraGasETH * price) / parseFloat(formatUnits(parseUnits('1', decimals), decimals));
135+
136+
// Convert to fixed decimal string to avoid scientific notation
137+
const extraGasAmountBN = parseUnits(extraGasInToken.toFixed(decimals), decimals);
138+
netFeeAmount = fees + extraGasAmountBN;
139+
}
140+
const netFeeFormatted = formatUnits(netFeeAmount, decimals);
141+
const netFeeUSD = getUsdBalance(price, netFeeFormatted, decimals);
142+
const netFeeText = `${parseFloat(netFeeFormatted).toString()} ${symbol} (~$${parseFloat(netFeeUSD.replace('$', '')).toFixed(2)} USD)`;
143+
const netFeeTooltip = `${formatFullPrecision(netFeeAmount, decimals)} ${symbol}`;
144+
145+
const totalText = `~${amount.slice(0, 6)} ${symbol} (~$${parseFloat(amountUSD.replace('$', '')).toFixed(2)} USD)`;
114146
const totalAmountBN = parseUnits(amount, decimals);
115147
const totalTooltip = `${formatFullPrecision(totalAmountBN, decimals)} ${symbol}`;
116148

@@ -157,12 +189,6 @@ export const DataSection = () => {
157189
<Value variant='body2'>{feesCollector}</Value>
158190
</Tooltip>
159191
</Row>
160-
<Row>
161-
<Label variant='body2'>Fees:</Label>
162-
<Tooltip title={feeTooltip} placement='top'>
163-
<Value variant='body2'>{feeText}</Value>
164-
</Tooltip>
165-
</Row>
166192
{actionType === EventType.WITHDRAWAL && (isQuoteValid || isExpired) && (
167193
<Row>
168194
<Label variant='body2'>Quote expires:</Label>
@@ -179,6 +205,40 @@ export const DataSection = () => {
179205
<Value variant='body2'>{valueText}</Value>
180206
</Tooltip>
181207
</Row>
208+
209+
{/* Net Fee row with dropdown for withdrawals */}
210+
{actionType === EventType.WITHDRAWAL && isQuoteValid && quoteFeesBPS !== null && quoteBaseFeeBPS !== null && (
211+
<>
212+
<Row>
213+
<Label variant='body2'>Net Fee:</Label>
214+
<FeeRow>
215+
<Tooltip title={netFeeTooltip} placement='top'>
216+
<NetFeeValue isExtraGasEnabled={quoteState.extraGas} variant='body2'>
217+
{netFeeText}
218+
</NetFeeValue>
219+
</Tooltip>
220+
<ExpandIconButton
221+
onClick={() => setIsFeeBreakdownOpen(!isFeeBreakdownOpen)}
222+
expanded={isFeeBreakdownOpen}
223+
>
224+
<ExpandMoreIcon />
225+
</ExpandIconButton>
226+
</FeeRow>
227+
</Row>
228+
229+
{/* Collapsible Fee Breakdown */}
230+
<Collapse in={isFeeBreakdownOpen}>
231+
<FeeBreakdownContainer>
232+
<FeeBreakdown
233+
feeBPS={quoteFeesBPS}
234+
baseFeeBPS={quoteBaseFeeBPS}
235+
extraGasAmountETH={quoteExtraGasAmountETH}
236+
amount={amount}
237+
/>
238+
</FeeBreakdownContainer>
239+
</Collapse>
240+
</>
241+
)}
182242
</Stack>
183243
)}
184244

@@ -260,3 +320,36 @@ const FlashingExpiredTimer = styled(Value)(({ theme }) => ({
260320
},
261321
},
262322
}));
323+
324+
const FeeRow = styled('div')({
325+
display: 'flex',
326+
alignItems: 'center',
327+
gap: '4px',
328+
});
329+
330+
const NetFeeValue = styled(Value, {
331+
shouldForwardProp: (prop) => prop !== 'isExtraGasEnabled',
332+
})<{ isExtraGasEnabled?: boolean }>(({ theme, isExtraGasEnabled }) => ({
333+
color: isExtraGasEnabled ? theme.palette.success.main : theme.palette.text.primary,
334+
fontWeight: isExtraGasEnabled ? 600 : 400,
335+
}));
336+
337+
const ExpandIconButton = styled(IconButton, {
338+
shouldForwardProp: (prop) => prop !== 'expanded',
339+
})<{ expanded?: boolean }>(({ theme, expanded }) => ({
340+
padding: '2px',
341+
minWidth: '24px',
342+
minHeight: '24px',
343+
transform: expanded ? 'rotate(180deg)' : 'rotate(0deg)',
344+
transition: theme.transitions.create('transform', {
345+
duration: theme.transitions.duration.shortest,
346+
}),
347+
'& .MuiSvgIcon-root': {
348+
fontSize: '18px',
349+
},
350+
}));
351+
352+
const FeeBreakdownContainer = styled('div')({
353+
marginTop: '8px',
354+
marginLeft: '16px',
355+
});

0 commit comments

Comments
 (0)